這個做法在討論泛型演算法傳入的參數會影響演算法的效率。
以下是一個進行倒序的演算法,從建構函式可以看出演算法會將傳入的 IEnumerable<T>
複製到 sourceSequence
,之後取得 ReverseEnumerator
進行到序。
public sealed class ReverseEnumerable<T> : IEnumerable<T>
{
IEnumerable<T> sourceSequence;
IList<T> originalSequence;
public ReverseEnumerable(IEnumerable<T> sequence)
{
sourceSequence = sequence;
}
public IEnumerator<T> GetEnumerator()
{
if (originalSequence == null)
{
originalSequence = new List<T>();
foreach (T item in sourceSequence)
originalSequence.Add(item);
}
return new ReverseEnumerator(originalSequence);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
private class ReverseEnumerator : IEnumerator<T>
{
int currentIndex;
IList<T> collection;
public ReverseEnumerator(IList<T> srcCollection)
{
collection = srcCollection;
currentIndex = collection.Count;
}
public T Current => collection[currentIndex];
object IEnumerator.Current => this.Current;
public void Dispose()
{
}
public bool MoveNext()
{
return --currentIndex >= 0;
}
public void Reset()
{
currentIndex = collection.Count;
}
}
}
這個演算法關鍵的就是取得傳入參數的 IEnumerator
,但是這個演算法只有假設傳入的參數有實做 IEnumerable
介面,所以不管你實際傳入的
參數是什麼型別,編譯器只會讓你使用 IEnumerable
能用的那些方法,最後只能建立一個中繼的區域變數 originalSequence
因為 ReverseEnumerable
只支援有實做 IList
的參數,所以複製 originalSequence
是一個折衷的處理方法。
IEnumerable<T> sourceSequence;
IList<T> originalSequence;
public ReverseEnumerable(IEnumerable<T> sequence)
{
sourceSequence = sequence;
}
public IEnumerator<T> GetEnumerator()
{
if (originalSequence == null)
{
originalSequence = new List<T>();
foreach (T item in sourceSequence)
originalSequence.Add(item);
}
return new ReverseEnumerator(originalSequence);
}
我們可以優化一下上面的演算法,由於傳入參數只有最低要求實作 IEnumerable
就好,所以實際上執行期別可能為其它類型的參數,可以直接在建構函式
直接測試是否能轉型並直接賦值,這樣執行期別有實做 IList
就能直接略過下面的 if 檢查,或者直接添加額外的多載。
public ReverseEnumerable(IEnumerable<T> sequence)
{
sourceSequence = sequence;
originalSequence = sequence as IList<T>;
}
public ReverseEnumerable(IList<T> sequence)
{
sourceSequence = sequence;
originalSequence = sequence;
}
但還是有一些特例沒有實作IList<T>
但是有實做 ICollection<T>
的集合,我們可以多加一個 if 檢查這種特例。
public IEnumerator<T> GetEnumerator()
{
if (originalSequence == null)
{
if (sourceSequence is ICollection)
{
var source = sourceSequence as ICollection;
originalSequence = new List<T>(source.Count);
}
else
{
originalSequence = new List<T>();
}
}
foreach (T item in sourceSequence)
originalSequence.Add(item);
return new ReverseEnumerator(originalSequence);
}
最後還有一種特例就是 string 類別,我們可以再加一個 ReverseStringEnumerator
最後輸出一個倒序的 char 集合。
public IEnumerator<T> GetEnumerator()
{
if (sourceSequence is string)
{
return new ReverseStringEnumerator(sourceSequence as string) as IEnumerator<T>;
}
if (originalSequence == null)
{
if (sourceSequence is ICollection)
{
var source = sourceSequence as ICollection;
originalSequence = new List<T>(source.Count);
}
else
{
originalSequence = new List<T>();
}
}
foreach (T item in sourceSequence)
originalSequence.Add(item);
return new ReverseEnumerator(originalSequence);
}
private sealed class ReverseStringEnumerator : IEnumerator<char>
{
private string sourceSequence;
private int currentIndex;
public ReverseStringEnumerator(string source)
{
sourceSequence = source;
currentIndex = source.Length;
}
public char Current => sourceSequence[currentIndex];
public void Dispose()
{
}
object System.Collections.IEnumerator.Current
=> sourceSequence[currentIndex];
public bool MoveNext() => --currentIndex >= 0;
public void Reset() => currentIndex = sourceSequence.
Length;
}
Summary
這個做法主要在顯示如何用最少的約束條件,也能透過內部型別檢查獲得那些專屬於某型別的功能,而且這些檢查被隱藏在我們的泛型類別裡面, 並且就算沒有供特殊的類型檢查也能夠運行,這些特例檢查只是用來提示演算法運行速度。