[corefx註釋說]-System.Collections.Generic.Stack

對C#裏邊的基礎類庫有充分的好奇心,所以就心血來潮寫一下,這個就不定期更新了,想什麼時候寫就什麼時候寫好了。這裏弱弱的吐槽一下CSDN的博客。爲了以防萬一,會在我其他的博客做一下備份。

廢話不多說 切入正題:
github上的源代碼

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;

這裏的引用沒什麼好解釋的,Stack僅依賴於System,剩下的兩個引用是用來分析測試的。

類的聲明


[DebuggerTypeProxy(typeof(StackDebugView<>))]
[DebuggerDisplay("Count = {Count}")]
public class Stack<T> : IEnumerable<T>,
    System.Collections.ICollection,
    IReadOnlyCollection<T>

Attributes沒什麼好解釋的,用在Debug裏。
這裏聲明Stack這樣一個泛型類,繼承IEnumerable< T >,ICollection, IReadOnlyCollection< T >。三個接口。說明這個類支持foreach循環。是一個只讀集合。

字段

private T[] _array;     // Storage for stack elements
private int _size;           // Number of items in the stack.
private int _version;        // Used to keep enumerator in sync w/ collection.
private Object _syncRoot;

private const int DefaultCapacity = 4;

這裏是它的字段
包括:棧內元素存儲數組,棧內元素個數計數器,和用於同步的一些變量。
並且提供了一個默認的容量大小(4)


        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.Stack"]/*' />
        public Stack()
        {
            _array = Array.Empty<T>();
        }

        // Create a stack with a specific initial capacity.  The initial capacity
        // must be a non-negative number.
        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.Stack1"]/*' />
        public Stack(int capacity)
        {
            if (capacity < 0)
                throw new ArgumentOutOfRangeException("capacity", SR.ArgumentOutOfRange_NeedNonNegNumRequired);
            _array = new T[capacity];
        }

        // Fills a Stack with the contents of a particular collection.  The items are
        // pushed onto the stack in the same order they are read by the enumerator.
        //
        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.Stack2"]/*' />
        public Stack(IEnumerable<T> collection)
        {
            if (collection == null)
                throw new ArgumentNullException("collection");
            _array = EnumerableHelpers.ToArray(collection, out _size);
        }

這裏是是它的三個構造方法:
第一種構造方法是直接構造一個空的存儲數組,至於其它的字段就不管了。(值類型留給CLR就好辣!~)
第二種構造方法是構造一個具有初始容量的存儲數組。並且對容量大小做了相應的判斷(必須是自然數)
第三種構造方法是複製一個集合到自己的存儲數組。並且對源數據集合進行檢查(不得爲null)

屬性

        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.Count"]/*' />
        public int Count
        {
            get { return _size; }
        }

        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.IsSynchronized"]/*' />
        bool System.Collections.ICollection.IsSynchronized
        {
            get { return false; }
        }

        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.SyncRoot"]/*' />
        Object System.Collections.ICollection.SyncRoot
        {
            get
            {
                if (_syncRoot == null)
                {
                    System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
                }
                return _syncRoot;
            }
        }

接下來是三個屬性,沒什麼好說的,一個是獲取集合內元素個數,剩下的兩個是敷衍同步用的。。。

方法

void clean() //清除

具體作用是移除在棧內的所有元素。

        // Removes all Objects from the Stack.
        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.Clear"]/*' />
        public void Clear()
        {
            Array.Clear(_array, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references.
            _size = 0;
            _version++;
        }

這裏要提及的是並不需要具體的對棧內對象進行深層次的清理工作。垃圾回收會重新清理這些資源的。

bool Contains(T) //判斷是否存在該元素

        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.Contains"]/*' />
        public bool Contains(T item)
        {
            int count = _size;

            EqualityComparer<T> c = EqualityComparer<T>.Default;
            while (count-- > 0)
            {
                if (((Object)item) == null)
                {
                    if (((Object)_array[count]) == null)
                        return true;
                }
                else if (_array[count] != null && c.Equals(_array[count], item))
                {
                    return true;
                }
            }
            return false;
        }

這裏的代碼讓人感覺回到了Hack time。這裏要注意的是 Equals和 == 是不一樣的,我這裏就不說了(容易跑題),可以谷哥度娘找到這裏的區別。

void CopyTo(T[], int) //將棧內元素拷貝到指定的數組中

        // Copies the stack into an array.
        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.CopyTo"]/*' />
        public void CopyTo(T[] array, int arrayIndex)
        {
            if (array == null)
            {
                throw new ArgumentNullException("array");
            }

            if (arrayIndex < 0 || arrayIndex > array.Length)
            {
                throw new ArgumentOutOfRangeException("arrayIndex", SR.ArgumentOutOfRange_NeedNonNegNum);
            }

            if (array.Length - arrayIndex < _size)
            {
                throw new ArgumentException(SR.Argument_InvalidOffLen);
            }

            if (array != _array)
            {
                int srcIndex = 0;
                int dstIndex = arrayIndex + _size;
                for (int i = 0; i < _size; i++)
                    array[--dstIndex] = _array[srcIndex++];
            }
            else
            {
                // Legacy fallback in case we ever end up copying within the same array.
                Array.Copy(_array, 0, array, arrayIndex, _size);
                Array.Reverse(array, arrayIndex, _size);
            }
        }

看源代碼我們會知道,你要拷貝到的數組中,從該數組的接收位置算起,長度必須足夠容納下棧內的所有元素。並且爲了貫徹落實好棧的FILO的優良品質,複製時需要嚴肅認真地處理好順序(也就是說複製過去的是逆序的)。這裏很奇怪的是對待相同數組和不同數組的執行差異(對待相同數組估計是要做類內擴展用)

void System.Collections.ICollection.CopyTo(Array array, int arrayIndex) 複製到指定數組中

void System.Collections.ICollection.CopyTo(Array array, int arrayIndex)
        {
            if (array == null)
            {
                throw new ArgumentNullException("array");
            }

            if (array.Rank != 1)
            {
                throw new ArgumentException(SR.Arg_RankMultiDimNotSupported);
            }

            if (array.GetLowerBound(0) != 0)
            {
                throw new ArgumentException(SR.Arg_NonZeroLowerBound);
            }

            if (arrayIndex < 0 || arrayIndex > array.Length)
            {
                throw new ArgumentOutOfRangeException("arrayIndex", SR.ArgumentOutOfRange_NeedNonNegNum);
            }

            if (array.Length - arrayIndex < _size)
            {
                throw new ArgumentException(SR.Argument_InvalidOffLen);
            }

            try
            {
                Array.Copy(_array, 0, array, arrayIndex, _size);
                Array.Reverse(array, arrayIndex, _size);
            }
            catch (ArrayTypeMismatchException)
            {
                throw new ArgumentException(SR.Argument_InvalidArrayType);
            }
        }

這裏判斷了一下矩陣的秩以及下標和是否越界的情況。還是有維護Stack的FILO的特徵。

Enumerator GetEnumerator() 返回用於枚舉的對象

        // Returns an IEnumerator for this Stack.
        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.GetEnumerator"]/*' />
        public Enumerator GetEnumerator()
        {
            return new Enumerator(this);
        }

        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.IEnumerable.GetEnumerator"]/*' />
        /// <internalonly/>
        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return new Enumerator(this);
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new Enumerator(this);
        }

void TrimExcess() 節省Stack空間

        public void TrimExcess()
        {
            int threshold = (int)(((double)_array.Length) * 0.9);
            if (_size < threshold)
            {
                Array.Resize(ref _array, _size);
                _version++;
            }
        }

這裏有一個“緩衝”的判斷,即threshold如果是小於數組的物理長度的百分之90的時候才壓縮,否則就不壓縮了。這個比較重要,參見下面的數組容量遞增規則。

T Peek() 返回棧頂元素

        // Returns the top object on the stack without removing it.  If the stack
        // is empty, Peek throws an InvalidOperationException.
        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.Peek"]/*' />
        public T Peek()
        {
            if (_size == 0)
                throw new InvalidOperationException(SR.InvalidOperation_EmptyStack);
            return _array[_size - 1];
        }

T Pop() 彈出棧

        // Pops an item from the top of the stack.  If the stack is empty, Pop
        // throws an InvalidOperationException.
        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.Pop"]/*' />
        public T Pop()
        {
            if (_size == 0)
                throw new InvalidOperationException(SR.InvalidOperation_EmptyStack);
            _version++;
            T item = _array[--_size];
            _array[_size] = default(T);     // Free memory quicker.
            return item;
        }

這裏又感覺回到了engineer time(並不只做一件事)

void Push(T) 推入棧

        // Pushes an item to the top of the stack.
        // 
        /// <include file='doc\Stack.uex' path='docs/doc[@for="Stack.Push"]/*' />
        public void Push(T item)
        {
            if (_size == _array.Length)
            {
                Array.Resize(ref _array, (_array.Length == 0) ? DefaultCapacity : 2 * _array.Length);
            }
            _array[_size++] = item;
            _version++;
        }

這裏關注一下它的推入規則,當存儲數組的長度爲零時,就設置爲4(常量字段中有寫),否則就以兩倍的遞增速度進行遞增。

T[] ToArray() 將Stack轉化爲Array

        public T[] ToArray()
        {
            T[] objArray = new T[_size];
            int i = 0;
            while (i < _size)
            {
                objArray[i] = _array[_size - i - 1];
                i++;
            }
            return objArray;
        }

依舊是貫徹落實好FILO的好特徵。

類內結構體

        /// <include file='doc\Stack.uex' path='docs/doc[@for="StackEnumerator"]/*' />
        [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")]
        public struct Enumerator : IEnumerator<T>,
            System.Collections.IEnumerator

作用是枚舉對象

字段

 private Stack<T> _stack;
 private int _index;
 private int _version;
 private T _currentElement;

這裏定義了幾個字段,一個是棧,指針,當前指向的元素。

構造方法

internal Enumerator(Stack<T> stack)
{
    _stack = stack;
    _version = _stack._version;
    _index = -2;
    _currentElement = default(T);
}

這裏關鍵在於_version這裏。(就是跟stack同步)和index的取值意義。

方法

bool Dispose() 結束枚舉
public void Dispose()
{
    _index = -1;
}
bool MoveNext() 移動到下一個元素
/// <include file='doc\Stack.uex' path='docs/doc[@for="StackEnumerator.MoveNext"]/*' />
public bool MoveNext()
{
    bool retval;
    if (_version != _stack._version) throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion);
    if (_index == -2)
    {  // First call to enumerator.
        _index = _stack._size - 1;
        retval = (_index >= 0);
        if (retval)
            _currentElement = _stack._array[_index];
        return retval;
    }
    if (_index == -1)
    {  // End of enumeration.
        return false;
    }

    retval = (--_index >= 0);
    if (retval)
        _currentElement = _stack._array[_index];
    else
        _currentElement = default(T);
    return retval;
}
T Current 當前元素

這裏可以看一下index == -2時是枚舉沒開始。index == -1時是枚舉結束。

/// <include file='doc\Stack.uex' path='docs/doc[@for="StackEnumerator.Current"]/*' />
public T Current
{
    get
    {
        if (_index == -2) throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted);
        if (_index == -1) throw new InvalidOperationException(SR.InvalidOperation_EnumEnded);
        return _currentElement;
    }
}

Object System.Collections.IEnumerator.Current
{
    get
    {
        if (_index == -2) throw new InvalidOperationException(SR.InvalidOperation_EnumNotStarted);
        if (_index == -1) throw new InvalidOperationException(SR.InvalidOperation_EnumEnded);
        return _currentElement;
    }
}
Reset()

重置

void System.Collections.IEnumerator.Reset()
{
    if (_version != _stack._version) throw new InvalidOperationException(SR.InvalidOperation_EnumFailedVersion);
    _index = -2;
    _currentElement = default(T);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章