對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);
}