【愚公系列】2023年02月 .NET/C#知识点-委托、匿名方法、Lambda、泛型委托、表达式树的进化史

举报
愚公搬代码 发表于 2023/02/28 22:50:41 2023/02/28
【摘要】 前言在 .NET 中,委托是一种类型,它可以持有对一个或多个方法的引用,并允许将这些方法作为参数传递给其他方法。.NET 中的委托类似于 C 和 C++ 中的函数指针,但具有更高的类型安全性和其他功能。委托的概念最早可以追溯到早期的编程语言,例如Simula和Smalltalk。然而,C# 的委托实现受到了函数指针和C++中的函数对象的启发。在.NET框架中,委托最初被引入为事件处理程序的...

前言

在 .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 的结果并输出它。

请注意,表达式目录树的主要优点之一是它们是类型安全的。在上面的示例中,我们明确指定了参数和返回值的类型,因此编译器可以在编译时执行类型检查,从而避免在运行时出现类型错误。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。