Chapter 1 - Item 7 : Express Callbacks with Delegates

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

在撰寫程式時,委派常被用來通知外部執行的溝通介面;C# 提供三種委派型別。

1. Action<T>(輸入 T、回傳值為 void)
2. Predicate<T>(輸入T、回傳值為 bool)
3. Func<T, TResult>(輸入T、回傳值為 TResult)
Note:Predicate<T> 與 Func<T, bool> 在編譯器中並不相同,即便輸入型別皆為 T、回傳型別皆為 bool。

委派的物件內部擁有一個儲存方法記憶體位置的容器,並使用 += 與 -= 處理事件方法的掛載與卸載;當委派物件觸發方法(群)時,將依照掛載方法順序執行(chain)。當方法有一個以上時,需注意若僅是觸發委派;則只會回傳最後一個方法的結果,中間的結果會被忽略。若要個別取得方法結果需呼叫 GetInvocationList 逐一執行並取得結果。

範例程式碼:

internal class PredicateTest
{
    private List<int> _container = new List<int>( ) { 1, 2, 3, 4, 5 };

    public void lengthyOperation( Func<bool> pred )
    {
        foreach ( var item in _container )
        {
            // Do something
            Debug.WriteLine( $"lengthyOperation Do something at item : {item}." );
            
            if ( !pred( ) )
                return;
        }
    }

    public void lengthyOperation2( Func<bool> pred )
    {
        bool isContinue = true;

        foreach ( var item in _container )
        {
            // Do something
            Debug.WriteLine( $"lengthyOperation2 Do something at item : {item}." );

            foreach ( Func<bool> pr in pred.GetInvocationList( ) )
                isContinue &= pr( );

            if ( !isContinue )
                return;
        }
    }
}

Client 程式碼:

Func<bool> cp = ( ) => false;
cp += ( ) => true;

var x = new PredicateTest( );
x.lengthyOperation( cp );
x.lengthyOperation2( cp );

輸出:
lengthyOperation Do something at item : 1.
lengthyOperation Do something at item : 2.
lengthyOperation Do something at item : 3.
lengthyOperation Do something at item : 4.
lengthyOperation Do something at item : 5.
lengthyOperation2 Do something at item : 1.

結論:
1. 在設計方法時也可試著設計委派型別為引數,讓外部決定處理邏輯;增加設計彈性與封裝性。

2. 使用 GetInvocationList 取得個別方法的結果,否則只會回傳最後一個方法的執行結果。

3. GetInvocationList 回傳結果為 Delegate [ ],在 foreach 繞行時需明確宣告為 Func<bool>。