Chapter 4 - Item 31 : Create Composable APIs for Sequences

Effective C# (Covers C# 6.0), (includes Content Update Program): 50 Specific Ways to Improve Your C#, 3rd Edition By Bill Wagner 讀後心得

本節將介紹利用 yield return 語法,創建可組合的集合查詢。配合 IEnumerable<T> 回傳值,讓查詢結果得以重用,以及減少儲存空間消耗。

1. 利用 yield return 語法取得可重用的集合。

範例程式碼:    

public static void unique( IEnumerable<int> nums )
{
    var uniqueValues = new HashSet<int>( );

    foreach ( var num in nums )
    {
        if ( !uniqueValues.Contains( num ) )
        {
            uniqueValues.Add( num );
            Debug.WriteLine( num.ToString( ) );
        }
    }
}

此範例雖然可以正常執行,但是無法重用。若想對回傳集合做不同操作,需另外撰寫方法。然而利用 yield return 特性可以做到。

利用 yield return 語法改寫:

public static IEnumerable<int> uniqueV2( IEnumerable<int> nums )
{
    var uniqueValues = new HashSet<int>( );
    foreach ( var num in nums )
    {
        uniqueValues.Add( num );
        yield return num;
    }
}

用戶端程式碼:

foreach ( var num in uniqueV2( array ) )
    Debug.WriteLine( num.ToString( ) );

uniqueV2 現在回傳的型別為 IEnumerable<int>,用戶端可以自行決定其對內部元素操作。增加了重用性。
    
進一步改寫成泛型版本,讓方法更加一般化。

public static IEnumerable<T> uniqueV3<T>( IEnumerable<T> sequence )
{
    var uniqueValues = new HashSet<T>( );
    foreach ( var item in sequence )
    {
        uniqueValues.Add( item );
        yield return item;
    }
}

2. 利用 yield return 創建可組合的集合查詢。

範例:
    
增加一個篩選條件。

public static IEnumerable<int> square( IEnumerable<int> nums )
{
    foreach ( var num in nums )
        yield return num * num;
}

將其和 uniqueV2 組合。
    
用戶端程式碼:

foreach ( var num in square( uniqueV2( array ) ) )
    Debug.WriteLine( num.ToString( ) );
Note:由於 yield return 特性為需要取得值時才執行,延後執行確保取得當前最新的值;而在查詢期間也不會產生額外的集合副本(直接回傳原始集合內被查詢的元素副本),減少記憶體消耗。

3. 合併不同集合並回傳合併後的結果。

public static IEnumerable<string> zip( IEnumerable<string> first,
    IEnumerable<string> second )
{
    using ( var firstSequence = first.GetEnumerator( ) )
    {
        using ( var secondSequence = second.GetEnumerator( ) )
        {
            while ( firstSequence.MoveNext( ) && secondSequence.MoveNext( ) )
                yield return $"{firstSequence.Current}, {secondSequence.Current}";
        }
    }
}
結論:
1. 利用 yield return 回傳可重用的集合查詢結果。

2. 利用 yield return 組合不同查詢方式。

3. 利用 yield return 減少記憶體空間消耗。