Chapter 4 - Item 32 : Decouple Iterations from Actions, Predicates, and Functions

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

本節延續 Item 31,一般對集合的操作大概分為兩個部分:

1. 回傳符合條件的元素。

2. 針對特定元素執行指定方法。

利用 yield return 和委派的組合,可以達到以上要求。

首先,C# 語言提供了三種內建委派:

public delegate bool Predicate<in T>( T obj );
public delegate TResult Func<in T, out TResult>( T arg );
public delegate void Action<in T>( T obj );

接著利用幾個範例示範如何使用。

範例一:

public static IEnumerable<T> where<T>(
    IEnumerable<T> sequence,
    Predicate<T> filterFunc )
{
    if ( sequence == null )
        throw new ArgumentNullException( nameof( sequence ),
            "Sequence must not be null" );

    if ( filterFunc == null )
        throw new ArgumentNullException( nameof( filterFunc ),
            "Predicate must not be null" );

    foreach ( T item in sequence )
    {
        if ( filterFunc( item ) )
            yield return item;
    }
}

利用輸入端定義的 Predicate<T>,迭代檢查集合內符合條件(回傳 true)的元素。

範例二:

public static IEnumerable<T> everyNthItem<T>(
    IEnumerable<T> sequence, int period )
{
    if ( sequence == null )
        throw new ArgumentNullException( nameof( sequence ),
            "Sequence must not be null" );

    if ( period <= 0 )
        throw new ArgumentNullException( nameof( period ),
            "Period must be greater than zero" );

    var count = 0;

    foreach ( T item in sequence )
    {
        if ( ++count % period == 0 )
            yield return item;
    }
}

回傳符合 peirod 區間索引的元素。

範例三:

public static IEnumerable<T> select<T>(
    IEnumerable<T> sequence, Func<T, T> method )
{
    if ( sequence == null )
        throw new ArgumentNullException( nameof( sequence ),
            "Sequence must not be null" );

    if ( method == null )
        throw new ArgumentNullException( nameof( method ),
            "Func must not be null" );

    foreach ( T element in sequence )
        yield return method( element );
}

迭代集合並回傳經過 Func<T,T> 方法修飾後的元素。

現實情況也不一定只會回傳T,有可能需回傳其他型別的元素。

利用泛型方法改寫程式:

public static IEnumerable<Tout> select<Tin,Tout>(
    IEnumerable<Tin> sequence, Func<Tin, Tout> method )
{
    if ( sequence == null )
        throw new ArgumentNullException( nameof( sequence ),
            "Sequence must not be null" );

    if ( method == null )
        throw new ArgumentNullException( nameof( method ),
            "Func must not be null" );

    foreach ( Tin element in sequence )
        yield return method( element );
}

現在輸入集合元素型別為 Tin,回傳集合元素型別為 Tout。

結論:

1. 利用委派將集合迭代與元素操作兩者分離,依照不同輸入委派執行不同操作;集合重用性高。