【愚公系列】2023年02月 .NET/C#知识点-委托、匿名方法、Lambda、泛型委托、表达式树的进化史
前言
在 .NET 中,委托是一种类型,它可以持有对一个或多个方法的引用,并允许将这些方法作为参数传递给其他方法。.NET 中的委托类似于 C 和 C++ 中的函数指针,但具有更高的类型安全性和其他功能。
委托的概念最早可以追溯到早期的编程语言,例如Simula和Smalltalk。然而,C# 的委托实现受到了函数指针和C++中的函数对象的启发。
在.NET框架中,委托最初被引入为事件处理程序的机制。事件是一种广泛使用的编程模型,用于在程序中处理异步和交互性操作。事件可以被认为是类似于信号和槽机制的一种形式。
委托在.NET中被广泛使用,不仅用于事件处理程序,还用于Lambda表达式和LINQ查询等高级编程概念。它们使得在.NET框架中编写高效和易于理解的代码变得更加容易。
一、委托、匿名方法、Lambda、泛型委托、表达式树的进化史
1.委托
1.1 无返回值委托
// 创建委托实例
CallbackFunction callback = new CallbackFunction(CallbackMethod);
// 调用方法并传入委托作为回调参数
ProcessData(callback);
Console.ReadKey();
// 接收委托作为回调参数的方法
static void ProcessData(CallbackFunction callback)
{
for (int i = 1; i <= 10; i++)
{
// 调用委托,传入当前的循环计数器值
callback(i);
}
}
// 委托所指向的方法,用于在每次循环中输出计数器的值
static void CallbackMethod(int value)
{
Console.WriteLine("Callback called with value: " + value);
}
delegate void CallbackFunction(int value);
在上面的示例中,我们首先定义了一个委托类型 CallbackFunction,它接收一个 int 类型的参数并不返回任何值。接下来,我们创建了一个 CallbackFunction 类型的委托实例 callback,并将一个方法 CallbackMethod 作为参数传递给它。
接着,我们调用了 ProcessData 方法,并将 callback 委托作为回调参数传递给它。在 ProcessData 方法中,我们使用一个 for 循环来遍历整数 1 到 10,并在每次循环中调用 callback 委托,将当前的计数器值作为参数传递给它。
最后,我们定义了 CallbackMethod 方法,它将在每次委托被调用时被执行。在本例中,它只是简单地将传入的参数打印到控制台上。
总之,这个例子演示了如何使用 .NET 委托来实现简单的回调功能。
1.2 有返回值委托
using System;
MyDelegate add = AddNumbers;
MyDelegate subtract = SubtractNumbers;
int result1 = add(5, 3);
Console.WriteLine("5 + 3 = " + result1);
int result2 = subtract(5, 3);
Console.WriteLine("5 - 3 = " + result2);
static int AddNumbers(int x, int y)
{
return x + y;
}
static int SubtractNumbers(int x, int y)
{
return x - y;
}
delegate int MyDelegate(int x, int y);
在此示例中,我们定义了一个委托类型 MyDelegate,它接受两个 int 类型的参数并返回一个 int 类型的值。然后,我们创建了两个委托实例,分别指向两个不同的方法 AddNumbers 和 SubtractNumbers。这些方法分别接受两个参数并返回它们的和或差。
在 Main 方法中,我们调用了这些委托实例,并将它们作为函数来使用,传递了两个整数参数,并将结果存储在变量 result1 和 result2 中,然后将这些结果输出到控制台。
1.3 多播委托
public delegate void MulticastDelegateExample(int value);
public class MulticastDelegateExampleClass
{
public static void PrintNumber(int value)
{
Console.WriteLine("PrintNumber(): {0}", value);
}
public static void PrintMoney(int value)
{
Console.WriteLine("PrintMoney(): {0}", value);
}
public static void PrintHexadecimal(int value)
{
Console.WriteLine("PrintHexadecimal(): {0:X}", value);
}
public static void Main()
{
// Create a multicast delegate.
MulticastDelegateExample mde = new MulticastDelegateExample(PrintNumber);
// Add PrintMoney and PrintHexadecimal to invocation list.
mde += PrintMoney;
mde += PrintHexadecimal;
// Invoke the multicast delegate.
mde(123);
// Remove PrintHexadecimal from the invocation list.
mde -= PrintHexadecimal;
Console.WriteLine("-----");
// Invoke the multicast delegate again.
mde(123);
}
}
2.匿名方法
PrintDelegate printDelegate = delegate (string message)
{
Console.WriteLine("Anonymous method: " + message);
};
printDelegate("Hello, world!");
delegate void PrintDelegate(string message);
这个程序定义了一个委托类型 PrintDelegate,它接受一个字符串参数并返回 void。然后,在 Main 方法中,我们使用匿名方法创建一个 PrintDelegate 实例,并将其赋值给变量 printDelegate。匿名方法的代码块包含一个输出语句,它将字符串 "Anonymous method: " 和传入的消息连接起来输出到控制台。最后,我们调用 printDelegate,将字符串 “Hello, world!” 作为参数传递给它,匿名方法就会执行并输出 “Anonymous method: Hello, world!” 到控制台。
需要注意的是,在上面的代码中,匿名方法是使用 delegate 关键字创建的。匿名方法的语法与普通方法非常相似,不同之处在于它没有名称,而是直接作为委托类型的实例赋值给一个变量。
3.Lambda表达式
// 使用Lambda表达式定义一个方法并将其分配给委托
Calculate add = (x, y) => x + y;
// 使用Lambda表达式定义另一个方法并将其分配给委托
Calculate subtract = (x, y) => x - y;
// 调用委托以执行Lambda表达式所定义的方法
int result1 = add(5, 3);
int result2 = subtract(5, 3);
Console.WriteLine($"5 + 3 = {result1}");
Console.WriteLine($"5 - 3 = {result2}");
delegate int Calculate(int x, int y);
在上面的示例中,我们定义了一个名为Calculate的委托,它接受两个int类型的参数并返回一个int类型的结果。然后,我们使用Lambda表达式定义了两个方法(一个加法和一个减法),并将它们分配给委托add和subtract。
4.泛型委托
1、基本使用
using System;
// 创建一个 MyDelegate<int> 委托实例
MyDelegate<int> myIntDelegate = new MyDelegate<int>(Add);
int result = myIntDelegate(2, 3);
Console.WriteLine("2 + 3 = {0}", result);
// 创建一个 MyDelegate<string> 委托实例
MyDelegate<string> myStringDelegate = new MyDelegate<string>(Concatenate);
string str = myStringDelegate("Hello, ", "World!");
Console.WriteLine(str);
static int Add(int a, int b)
{
return a + b;
}
static string Concatenate(string a, string b)
{
return a + b;
}
// 定义一个泛型委托
delegate T MyDelegate<T>(T a, T b);
在这个示例中,我们首先定义了一个泛型委托 MyDelegate<T>,它接受两个类型为 T 的参数,并返回类型为 T 的值。然后,我们创建了两个委托实例,一个用于整数加法,另一个用于字符串连接。最后,我们分别调用这两个委托实例,并打印结果。
这个示例展示了泛型委托在.NET中的用法,它可以接受任何类型的参数和返回值,使得我们可以写出更加灵活的代码。
2、Action和Func
//Action无返回值、Func有返回值
//方法一:
Func<int, int, int> cAdd1 = (int x, int y) => { return x + y; };
int result1 = cAdd1(5, 6);
//方法二:
Func<int, int, int> cAdd2 = (x, y) => { return x + y; };
int result2 = cAdd2(5, 6);
//方法三:
Func<int, int, int> cAdd3 = (x, y) => x + y;
int result3 = cAdd2(5, 6);
Console.WriteLine(result3);
5.表达式树
1、编译使用
using System.Linq.Expressions;
Expression<Func<int, int, int>> exp = (x, y) => x + y;
Func<int, int, int> fun = exp.Compile();
int result = fun(2, 3);
Console.WriteLine(result);
2、拼接使用
// 定义参数
using System.Linq.Expressions;
ParameterExpression x = Expression.Parameter(typeof(int), "x");
ParameterExpression y = Expression.Parameter(typeof(int), "y");
// 创建表达式目录树
BinaryExpression sum = Expression.Add(x, y);
// 创建委托
Func<int, int, int> sumFunc = Expression.Lambda<Func<int, int, int>>(sum, x, y).Compile();
// 使用委托计算结果
int result = sumFunc(1, 2);
Console.WriteLine(result);
在这个例子中,我们首先定义了两个参数 x 和 y,它们分别表示要相加的两个整数。然后,我们创建了一个 BinaryExpression 对象,它表示将 x 和 y 相加的操作。接着,我们使用 Expression.Lambda 方法将表达式目录树转换为一个委托,并编译它以生成可执行代码。最后,我们使用生成的委托计算 1 + 2 的结果并输出它。
请注意,表达式目录树的主要优点之一是它们是类型安全的。在上面的示例中,我们明确指定了参数和返回值的类型,因此编译器可以在编译时执行类型检查,从而避免在运行时出现类型错误。
- 点赞
- 收藏
- 关注作者
评论(0)