c# Stack源码解析

Stack是个泛型栈类,内部的实现很简单,就是用数组实现的,主要提供了Push(),Pop(),Peek(),ToArray(),CopyTo()等方法提供给使用者使用,话不多说,我们先来看看Stack的类头:

public class Stack : IEnumerable, 
        System.Collections.ICollection,
        IReadOnlyCollection {
        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.
#if !SILVERLIGHT
        [NonSerialized]
#endif
        private Object _syncRoot;
                
        private const int _defaultCapacity = 4;
        static T[] _emptyArray = new T[0];
        
        /// 
        public Stack() {
            _array = _emptyArray;
            _size = 0;
            _version = 0;
        }
    
        // Create a stack with a specific initial capacity.  The initial capacity
        // must be a non-negative number.
        /// 
        public Stack(int capacity) {
            if (capacity < 0)
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired);
            _array = new T[capacity];
            _size = 0;
            _version = 0;
        }
可以看到主要就声明了一个T类型的数组_array,_size是用来存储当前使用的栈元素个数,_version用来记录版本号(用来在foreach的时候进行判断,任何对栈的更改都会使版本号+1,如果循环中版本号有变化,会抛出异常)。_defaultCapacity为默认在第一次为栈添加元素时创建的数组大小。Stack默认的无参构造函数会将数组_array指向静态数组_emptyArray,也可以在创建Stack时指定栈数组的初始大小,这样Stack会直接创建相应大小的数组元素(这样的好处是:在已知栈大小的情况下,指定栈数组的大小,可以减少数组空间不够时,重新创建新的数组和拷贝的消耗)。

接下来,我们来看看Push()函数:

public void Push(T item) {
            if (_size == _array.Length) {
                T[] newArray = new T[(_array.Length == 0) ? _defaultCapacity : 2*_array.Length];
                Array.Copy(_array, 0, newArray, 0, _size);
                _array = newArray;
            }
            _array[_size++] = item;
            _version++;
        }
当数组填满时,如果初次创建,则创建默认大小,否则,将当前数组扩容为原来的两倍,并将原数组的内容拷贝到新数组中。接着讲值放置在数组的末尾,版本号+1。

Peek()和Pop()函数:

public T Peek() {
            if (_size==0)
                ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyStack);
            return _array[_size-1];
        }
    
        // Pops an item from the top of the stack.  If the stack is empty, Pop
        // throws an InvalidOperationException.
        /// 
        public T Pop() {
            if (_size == 0)
                ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyStack);
            _version++;
            T item = _array[--_size];
            _array[_size] = default(T);     // Free memory quicker.
            return item;
        }
Peek和Pop的差别在于,Peek只是返回栈顶,Pop是返回栈顶元素的同时并将其删除,所以Peek实质并没有对数据进行更改,不会增加版本号,可以在foreach中使用,而Pop对数据做出了修改,会导致版本号+1。
ToArray函数:

public T[] ToArray()
        {
            T[] objArray = new T[_size];
            int i = 0;
            while(i < _size) {
                objArray[i] = _array[_size-i-1];
                i++;
            }
            return objArray;
        }
很简单,就是便利所有的元素,进行到新建的数组中,唯一注意的一点是,得到的数组和Stack中的数组顺序是相反的,也就是说栈顶的元素放在了返回的数组的头部。
Contains函数:

public bool Contains(T item) {
            int count = _size;
 
            EqualityComparer c = EqualityComparer.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;
        }
从这段代码我们可以看出Stack是允许插入null值的,翻回到前面的Push(),我们也可以看到,插入的时候并没有对数据做任何的约束。

下面是Stack类的所有源码:

namespace System.Collections.Generic {
    using System;
    using System.Diagnostics;
    using System.Diagnostics.CodeAnalysis;
    using System.Security.Permissions;
 
    // A simple stack of objects.  Internally it is implemented as an array,
    // so Push can be O(n).  Pop is O(1).
 
    [DebuggerTypeProxy(typeof(System_StackDebugView<>))]
    [DebuggerDisplay("Count = {Count}")]
#if !SILVERLIGHT
    [Serializable()]    
#endif
    [System.Runtime.InteropServices.ComVisible(false)]
    public class Stack : IEnumerable, 
        System.Collections.ICollection,
        IReadOnlyCollection {
        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.
#if !SILVERLIGHT
        [NonSerialized]
#endif
        private Object _syncRoot;
                
        private const int _defaultCapacity = 4;
        static T[] _emptyArray = new T[0];
        
        /// 
        public Stack() {
            _array = _emptyArray;
            _size = 0;
            _version = 0;
        }
    
        // Create a stack with a specific initial capacity.  The initial capacity
        // must be a non-negative number.
        /// 
        public Stack(int capacity) {
            if (capacity < 0)
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired);
            _array = new T[capacity];
            _size = 0;
            _version = 0;
        }
    
        // 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.
        //
        /// 
        public Stack(IEnumerable collection) 
        {
            if (collection==null)
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
 
            ICollection c = collection as ICollection;
            if( c != null) {
                int count = c.Count;
                _array = new T[count];
                c.CopyTo(_array, 0);  
                _size = count;
            }    
            else {                
                _size = 0;
                _array = new T[_defaultCapacity];                    
                
                using(IEnumerator en = collection.GetEnumerator()) {
                    while(en.MoveNext()) {
                        Push(en.Current);                                    
                    }
                }
            }
        }
    
        /// 
        public int Count {
            get { return _size; }
        }
            
        /// 
        bool System.Collections.ICollection.IsSynchronized {
            get { return false; }
        }
 
        /// 
        Object System.Collections.ICollection.SyncRoot {
            get { 
                if( _syncRoot == null) {
                    System.Threading.Interlocked.CompareExchange(ref _syncRoot, new Object(), null);    
                }
                return _syncRoot;                 
            }
        }
    
        // Removes all Objects from the Stack.
        /// 
        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++;
        }
 
        /// 
        public bool Contains(T item) {
            int count = _size;
 
            EqualityComparer c = EqualityComparer.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;
        }
    
        // Copies the stack into an array.
        /// 
        public void CopyTo(T[] array, int arrayIndex) {
            if (array==null) {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
            }
 
            if (arrayIndex < 0 || arrayIndex > array.Length) {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.arrayIndex, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
            }
 
            if (array.Length - arrayIndex < _size) {
                ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
            }
    
            Array.Copy(_array, 0, array, arrayIndex, _size);
            Array.Reverse(array, arrayIndex, _size);
        }
 
        void System.Collections.ICollection.CopyTo(Array array, int arrayIndex) {
            if (array==null) {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
            }
 
            if (array.Rank != 1) {
                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_RankMultiDimNotSupported);
            }
 
            if( array.GetLowerBound(0) != 0 ) {
                ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_NonZeroLowerBound);
            }
 
            if (arrayIndex < 0 || arrayIndex > array.Length) {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.arrayIndex, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
            }
 
            if (array.Length - arrayIndex < _size) {
                ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
            }
            
            try {                
                Array.Copy(_array, 0, array, arrayIndex, _size);
                Array.Reverse(array, arrayIndex, _size);
            }
            catch(ArrayTypeMismatchException){
                ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArrayType);
            }
        }
    
        // Returns an IEnumerator for this Stack.
        /// 
        public Enumerator GetEnumerator() {
            return new Enumerator(this);
        }
 
        /// 
        /// 
        IEnumerator IEnumerable.GetEnumerator() {
            return new Enumerator(this);
        }
 
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
            return new Enumerator(this);
        }
 
        public void TrimExcess() {
            int threshold = (int)(((double)_array.Length) * 0.9);             
            if( _size < threshold ) {
                T[] newarray = new T[_size];
                Array.Copy(_array, 0, newarray, 0, _size);    
                _array = newarray;
                _version++;
            }
        }    
 
        // Returns the top object on the stack without removing it.  If the stack
        // is empty, Peek throws an InvalidOperationException.
        /// 
        public T Peek() {
            if (_size==0)
                ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyStack);
            return _array[_size-1];
        }
    
        // Pops an item from the top of the stack.  If the stack is empty, Pop
        // throws an InvalidOperationException.
        /// 
        public T Pop() {
            if (_size == 0)
                ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyStack);
            _version++;
            T item = _array[--_size];
            _array[_size] = default(T);     // Free memory quicker.
            return item;
        }
    
        // Pushes an item to the top of the stack.
        // 
        /// 
        public void Push(T item) {
            if (_size == _array.Length) {
                T[] newArray = new T[(_array.Length == 0) ? _defaultCapacity : 2*_array.Length];
                Array.Copy(_array, 0, newArray, 0, _size);
                _array = newArray;
            }
            _array[_size++] = item;
            _version++;
        }
    
        // Copies the Stack to an array, in the same order Pop would return the items.
        public T[] ToArray()
        {
            T[] objArray = new T[_size];
            int i = 0;
            while(i < _size) {
                objArray[i] = _array[_size-i-1];
                i++;
            }
            return objArray;
        }
 
        /// 
#if !SILVERLIGHT
        [Serializable()]
#endif
        [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes", Justification = "not an expected scenario")]
        public struct Enumerator : IEnumerator,
            System.Collections.IEnumerator
        {
            private Stack _stack;
            private int _index;
            private int _version;
            private T currentElement;
    
            internal Enumerator(Stack stack) {
                _stack = stack;
                _version = _stack._version;
                _index = -2;
                currentElement = default(T);
            }
 
            /// 
            public void Dispose()
            {
                _index = -1;
            }
 
            /// 
            public bool MoveNext() {
                bool retval;
                if (_version != _stack._version) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.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;
            }
    
            /// 
            public T Current {
                get {
                    if (_index == -2) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted);
                    if (_index == -1) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded);
                    return currentElement;
                }
            }
 
            Object System.Collections.IEnumerator.Current {
                get {
                    if (_index == -2) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumNotStarted);
                    if (_index == -1) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumEnded);
                    return currentElement;
                }
            }
 
            void System.Collections.IEnumerator.Reset() {
                if (_version != _stack._version) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
                _index = -2;
                currentElement = default(T);
            }
        }    
    }
} 
  


你可能感兴趣的:(c#)