C# 進階委派(delegate)介紹: Action 、 Func 和 Predicate

C# 進階委派(delegate)介紹: Action 、 Func 和 Predicate
書接上回,在去年的 C# 委派(delegate) 介紹 這篇中了解到委派的使用方式,但是每次使用都要定義一個委派和一個委派實例,用起來有點麻煩,那有沒有更簡單的方式呢?

原始 delegate 寫法

    
public class MyClass1
{
    public delegate void OutputHandler(string output);

    public OutputHandler? Output;
}
    

使用方式:
    
MyClass1 myClass1 = new MyClass1();
myClass1.Output += (output) =>
{
    Console.WriteLine($"myClass1.Output: {output}");
};
myClass1.Output?.Invoke("Hello, World!");
    

Action

    
public class MyClass2
{
    public Action<string>? Output;
}
    

使用方式:
    
MyClass2 myClass2 = new MyClass2();
myClass2.Output += (output) =>
{
    Console.WriteLine($"myClass2.Output: {output}");
};
myClass2.Output?.Invoke("Hello, World!");

    

由上可知, delegate 和 Action 在使用的方式是一樣的,差別在於宣告方式。

補充: Action 空的實例化方式如下:
    
public Action<string>? Output = new Action<string>((str) => { });
    

在 .NET 6 中可以簡化為:
    
public Action<string>? Output = _ => { }; 
    

Func

Action 和 Func 的差異在於 Action 沒有回傳值,而 Func 有回傳值。Func 的最後一個參數就是回傳值的型態。宣告方式:
    
public class MyClass3
{
    /// <summary>
    /// 傳入 string,回傳 int
    /// </summary>
    public Func<string, int>? Output;
}
    

使用方式:
    
MyClass3 myClass3 = new MyClass3();
myClass3.Output += (output) =>
{
    Console.WriteLine($"myClass3.Output: {output}");
    return output.Length;
};
int? result = myClass3.Output?.Invoke("Hello, World!");
Console.WriteLine($"result: {result}");


/*
myClass3.Output: Hello, World!
result: 13
*/
    

Predicate

Predicate 可以理解為就是 Func ,只是回傳的參數類型固定是 bool
    
public class MyClass4
{
    /// <summary>
    /// 傳入 string,回傳 bool
    /// </summary>
    public Predicate<string>? Output;
}
    

使用方式:
    
MyClass4 myClass4 = new MyClass4();
myClass4.Output += (output) =>
{
    if (string.IsNullOrWhiteSpace(output)) return false;
    Console.WriteLine($"myClass4.Output: {output}");
    return true;
};
bool? result = myClass4.Output?.Invoke("Hello, World!");
Console.WriteLine($"result: {result}");

/*
myClass4.Output: Hello, World!
result: True
*/
    

結論

其實 Action 、 Func 和 Predicate 都只是把最原始的 delegate 進行包裝,變成語法糖方便使用而已,在上篇學會 delegate 後就會發現這三個沒有什麼難的,反而是讓 delegate 的使用更加的方便。

再次補充:
上面委派使用時的程式碼可以省略 Invoke ,直接簡寫為:
    

// Output?.Invoke("Hello, World!");
Output("Hello, World!");
    

但是在找不到委派實例的時候會拋出例外:
    

Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
    

所以還是建議使用 ?.Invoke 的方式,在執行前先檢查是否為空。

參考資料:
Microsoft.Learn - Func<TResult> Delegate
Microsoft.Learn - Action<T> Delegate
Microsoft.Learn - Predicate<T> Delegate
StackOverflow - Is using Action.Invoke considered best practice?

留言