委托是 C# 编程中最重要和最强大的功能之一。它们允许我们将方法作为参数传递,将方法存储为变量,并创建自定义事件和回调。在本文中,我们将探讨两种特殊类型的委托:Action 和 Func。这些委托在 .NET 中预定义,可以简化代码并使其更具可读性和可维护性。
委托是表示对方法的引用的类型。委托可以指向与委托具有相同签名的任何方法,这意味着相同数量的参数和类型以及相同的返回类型。委托还可以指向多个方法,形成委托链或多播委托。
声明委托的基本语法:
// Declare a delegate type
delegate <return type> <delegate name> (<parameters>);
// Example of a delegate that can point to any method that takes an int and returns a string
delegate string IntToString(int x);
要使用委托,我们需要创建它的实例并为其分配一个方法。我们可以直接使用方法名称,也可以使用关键字并将方法名称作为参数传递。我们还可以使用匿名方法或 lambda 表达式来创建委托实例:
// A method that takes an int and returns a string
static string IntToStringMethod(int number) {
return "The number is " + number;
}// Create a delegate instance and assign it a method
// using method name
IntToString myDelegate = IntToStringMethod;
//or using new keyword
IntToString myDelegate = new IntToString(IntToStringMethod);// Invoke the delegate
// calls IntToStringMethod(10) and returns "The number is 10"
string result = myDelegate(10);// Create a delegate instance using an anonymous method
IntToString myDelegate = delegate(int x) {
return "The number is " + x;
};// Create a delegate instance using a lambda expression
IntToString myDelegate = x => "The number is " + x;
以上是声明和使用委托的基本语法。我们可以将委托用于各种目的:
让我们更详细地探讨操作委托和功能委托。
操作委托是预定义的委托,可以指向任何采用零个或多个参数并返回 void(无值)的方法。操作委托在 System 命名空间中定义,并具有以下通用形式:
// Action delegate with no parameters
public delegate void Action();
// Action delegate with one parameter
public delegate void Action<T>(T obj);
// Action delegate with two parameters
public delegate void Action<T1, T2>(T1 arg1, T2 arg2);
// Action delegate with three parameters
public delegate void Action<T1, T2, T3>(T1 arg1, T2 arg2, T3 arg3);
// ... and so on, up to 16 parameters
以上是声明 Action 委托的基本语法。我们还可以使用关键字让编译器推断委托类型,并避免显式类型声明。使用 Action 委托的优点是,我们不需要为返回 void 的方法声明自己的委托类型。我们可以简单地使用预定义的 Action 委托并将参数类型指定为泛型参数。这可以使我们的代码更加简洁和一致。
例如,假设我们有一个将消息打印到控制台的方法:
// A method that prints a message
public static void PrintMessage(string message) {
Console.WriteLine(message);
}
要将此方法用作委托,我们可以声明自己的委托类型,也可以使用 Action 委托:
// Declare delegate type
delegate void PrintDelegate(string message);
// Create a delegate instance
PrintDelegate print = PrintMessage;
// Invoke the delegate
print("Hello, world!");
// Or, use an Action delegate
Action<string> printWithAction = PrintMessage;
// Invoke the delegate
printWithAction("Hello, world!");
正如我们所看到的,使用 Action 委托可以省去声明委托类型的麻烦,并使代码更具可读性。
我们还可以使用匿名方法或 lambda 表达式来创建 Action 委托实例:
// Create an Action delegate instance using an anonymous method
Action<string> print = delegate(string message) {
Console.WriteLine(message);
};
// Create an Action delegate instance using a lambda expression
Action<string> print = message => Console.WriteLine(message);
上面的代码演示了如何使用匿名方法和 lambda 表达式创建委托的实例。Action
让我们继续讨论 func 代表。
Func 委托是预定义的委托,可以指向采用零个或多个参数并返回任何类型的值的任何方法。Func 委托在 System 命名空间中定义,并具有以下通用形式:
// Func delegate with no parameters and a return value
public delegate TResult Func<out TResult>();
// Func delegate with one parameter and a return value
public delegate TResult Func<in T, out TResult>(T arg);
// Func delegate with two parameters and a return value
public delegate TResult Func<in T1, in T2, out TResult>(T1 arg1, T2 arg2);
// Func delegate with three parameters and a return value
public delegate TResult Func<in T1, in T2, in T3, out TResult>(T1 arg1, T2 arg2, T3 arg3);
// ... and so on, up to 16 parameters and a return value
使用 Func 委托的优点是,我们不需要为返回值的方法声明自己的委托类型。我们可以简单地使用预定义的 Func 委托,并将参数类型和返回类型指定为泛型参数。这可以使我们的代码更加简洁和一致。
例如,假设我们有一个计算数字平方的方法:
// A method that calculates the square of a number
public static int Square(int x) {
return x * x;
}
要将此方法用作委托,我们可以声明自己的委托类型,也可以使用 Func 委托:
// Declare delegate type
delegate int SquareDelegate(int x);
// Create a delegate instance
SquareDelegate square = Square;
// Invoke the delegate
int result = square(10); // returns 100
// Or, use a Func delegate
Func<int, int> square = Square;
// Invoke the delegate
int result = square(10); // returns 100
正如我们所看到的,使用 Func 委托省去了声明委托类型的麻烦,并使代码更具可读性。
我们还可以使用匿名方法或 lambda 表达式来创建 Func 委托实例:
// Create a Func delegate instance using an anonymous method
Func<int, int> square = delegate(int x) {
return x * x;
};
// Create a Func delegate instance using a lambda expression
Func<int, int> square = x => x * x;
上面的代码演示了如何使用匿名方法和 lambda 表达式创建委托的实例。Func
让我们比较一下 Action 和 Func 委托。
Action 和 Func 委托非常相似,但有一个区别:Action 委托返回 void,而 Func 委托返回值。这种差异会影响我们在代码中使用这些委托的方式:
使用 Action 和 Func 委托可以提高代码的可读性和可维护性,但它们也有一些性能影响。在使用这些委托时,我们应该意识到这些影响并做出明智的决定:
Action 和 Func 委托非常通用,可用于许多方案。下面是可以使用这些委托的实际方案的一些示例:
// A collection of numbers
int[] numbers = { 1, 2, 3, 4, 5 };
// Use LINQ to filter and project the numbers
var query = numbers.Where(x => x % 2 == 0) // use a Func delegate to filter even numbers
.Select(x => x * x); // use a Func delegate to project each number to its square
// Execute the query and print the results
foreach (var item in query) {
Console.WriteLine(item);
}
上述代码的输出为 4、16。该方法根据谓词过滤数字,该谓词对偶数返回 true,对奇数返回 false。该方法使用 lambda 表达式将每个数字投影到其平方。Wherex => x % 2 == 0Selectx => x * x
// A method that performs some long-running work
public static void DoWork() {
Console.WriteLine("Doing work...");
Thread.Sleep(5000); // simulate some work
Console.WriteLine("Work done.");
}
// A method that performs some continuation work
public static void DoMoreWork(Task t) {
Console.WriteLine("Doing more work...");
Thread.Sleep(3000); // simulate some work
Console.WriteLine("More work done.");
}
// Use TPL to run the methods in parallel
var task = Task.Run((Action)DoWork); // use an Action delegate to start a new task
task.ContinueWith(DoMoreWork); // use an Action delegate to attach a continuation task
// Wait for the tasks to finish
task.Wait();
上述代码的输出如下所示。该方法启动一个新任务,该任务在单独的线程中执行该方法。该方法附加一个延续任务,该任务在任务完成后执行该方法。
输出:
Doing work...
Work done.
Doing more work...
More work done.
// A method that handles the web response
public static async Task HandleResponse(HttpResponseMessage response) {
Console.WriteLine("Response status code: " + response.StatusCode);
string content = await response.Content.ReadAsStringAsync();
Console.WriteLine("Response content: " + content);
}
// A method that handles the web exception
public static void HandleException(Exception ex) {
Console.WriteLine("Exception: " + ex.Message);
}
// Use HttpClient to send an asynchronous web request
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com");
client.SendAsync(request)
// use a Func delegate to handle the response
.ContinueWith((Func<Task<HttpResponseMessage>, Task>)HandleResponse, TaskContinuationOptions.OnlyOnRanToCompletion)
// use an Action delegate to handle the exception
.ContinueWith((Action<Task>)HandleException, TaskContinuationOptions.OnlyOnFaulted);
上面的代码使用该类向 URL 发送异步 Web 请求。该方法附加一个在请求成功完成时执行该方法的延续任务,以及另一个在请求失败并出现异常时执行该方法的延续任务。
让我们探讨有效使用 Action 和 Func 委托的技巧和最佳实践,以及要避免的常见陷阱。
下面是在 C# 项目中有效使用 Action 和 Func 委托的一些提示和最佳实践:
下面是一些常见的陷阱,以及如何在 C# 项目中使用 Action 和 Func 委托时避免这些陷阱:
在本文中,我们了解了 C# 中的 Action 和 Func 委托,以及它们如何简化我们的代码并使其更具可读性和可维护性。我们还看到了一些实际场景的示例,在这些场景中,这些委托很有用,以及一些有效使用它们的技巧和最佳实践。我们还讨论了一些性能注意事项和权衡,以及一些常见的陷阱以及如何避免它们。
Action 和 Func 委托是强大的工具,可以帮助我们用 C# 编写富有表现力和简洁的代码。它们能够将方法作为参数传递,将方法存储为变量,以及创建自定义事件和回调。它们还广泛用于许多 .NET 库和框架,例如 LINQ 和 TPL。