Effective C# (Covers C# 6.0), (includes Content Update Program): 50 Specific Ways to Improve Your C#, 3rd Edition By Bill Wagner 讀後心得
此節是 Item 18 的延伸,利用更明確的判斷執行階段型別;撰寫更有效率的程式碼。
以下用一個反轉列舉器做為例子。
寫法一:
public sealed class ReverseEnumerable<T> : IEnumerable<T>
{
private IList<T> _originalSequence;
private IEnumerable<T> _sourceSequence;
public ReverseEnumerable( IEnumerable<T> sourceSequence )
{
_sourceSequence = sourceSequence;
}
IEnumerator IEnumerable.GetEnumerator( ) => GetEnumerator( );
public IEnumerator<T> GetEnumerator( )
{
if ( _originalSequence == null )
{
_originalSequence = new List<T>( );
foreach ( var item in _sourceSequence )
_originalSequence.Add( item );
}
return new ReverseEnumerator( _originalSequence );
}
private class ReverseEnumerator : IEnumerator<T>
{
private IList<T> _collection;
private int _currentIndex;
public T Current => _collection [ _currentIndex ];
object IEnumerator.Current => Current;
public ReverseEnumertor( IList<T> collection )
{
_collection = collection;
_currentIndex = collection.Count;
}
public void Dispose( )
{
// No implement but necessary,
// because IEnumerator<T> implements IDisposable.
// No protected Dispose( bool isDisposing ) needed,
// because this class is sealed;
}
public bool MoveNext( ) => --_currentIndex >= 0;
public void Reset( ) => _currentIndex = _collection.Count;
}
}
寫法一有一處可以改進,在 ReverseEnumerable<T> 建構子處;我們初始化了一個不給定長度的 List。由於輸入的容器只要有實作 ICollection<T> 就可取得容器中含有的數量(實務上極有可能),若能事先知道數量就能省下複製陣列元素和垃圾回收的次數;知道了優化的原因與目的之後,接著改寫程式碼。
寫法二(只修改建構子部分):
public IEnumerator<T> GetEnumerator( )
{
if ( _originalSequence == null )
{
var source = _sourceSequence as ICollection<T>;
if ( source != null )
_originalSequence = new List<T>( source.Count );
else
_originalSequence = new List<T>( );
foreach ( var item in _sourceSequence )
_originalSequence.Add( item );
}
return new ReverseEnumerator( _originalSequence );
}
寫法二建構子新增了輸入型別判斷式,若能夠成功轉型為 ICollection<T>;則利用該介面的實作屬性 Count 取得容器中數量,省去了後續多餘的操作。
然而寫法二仍有一些邊界條件沒有考慮到,string 類別也支援了隨機存取的功能;但並沒有實作 ICollection<T>。因此若要支援的更全面,需要增加定義以符合 string 需求。
寫法三:
新增 ReversStringEnumerator : IEnumertator<char>。
private sealed class ReverseStringEnumerator : IEnumerator<char>
{
private int _currentIndex;
private string _sourceSequence;
public char Current => _sourceSequence [ _currentIndex ];
object IEnumerator.Current => Current;
public ReverseStringEnumerator( string source )
{
_sourceSequence = source;
_currentIndex = source.Length;
}
public void Dispose( )
{
// No implement but necessary,
// because IEumerator<T> implements IDisposable.
}
public bool MoveNext( ) => --_currentIndex >= 0;
public void Reset( ) => _currentIndex = _sourceSequence.Length;
}
Note:除了容器中數量是呼叫 _sourceSequence.Lengh 而非 _sourceSequence.Count 外,其餘幾乎相同。
ReverseEnumerable<T> 建構子也需做對應的修改。
public IEnumerator<T> GetEnumerator( )
{
if ( _originalSequence == null )
{
var sourceString = _sourceSequence as string;
// Note that cast because T may not be a char at compile time.
if ( sourceString != null )
return new ReverseStringEnumerator( sourceString ) as IEnumerator<T>;
var source = _sourceSequence as ICollection<T>;
if ( source != null )
_originalSequence = new List<T>( source.Count );
else
_originalSequence = new List<T>( );
foreach ( var item in _sourceSequence )
_originalSequence.Add( item );
}
return new ReverseEnumerator( _originalSequence );
}
ReverseEnumerable<T> 建構子新增了 string 型別的判斷。如此一來,這個反轉列舉器才算完整。
結論:
1. 利用執行階段的型別判斷,減少泛型帶來的可能效能損耗。
2. 考慮各種輸入可能,讓泛型支援更完整。
1. 利用執行階段的型別判斷,減少泛型帶來的可能效能損耗。
2. 考慮各種輸入可能,讓泛型支援更完整。