【C#】委托/Lambda/事件

目录

  • [0 参考文章](#0 参考文章)
  • [1 委托(delegate)](#1 委托(delegate))
    • [1.1 委托的用途](#1.1 委托的用途)
    • [1.2 委托的声明](#1.2 委托的声明)
      • [1.2.1 自定义委托](#1.2.1 自定义委托)
      • [1.2.2 System.Func和System.Action](#1.2.2 System.Func和System.Action)
    • [1.3 委托是一种特殊的类](#1.3 委托是一种特殊的类)
  • [2 Lambda表达式](#2 Lambda表达式)
  • [3 事件(event)](#3 事件(event))

0 参考文章

知乎:事件是以特殊方式声明的委托字段吗

书籍:《C# 7.0本质论》

1 委托(delegate)

为了方便理解,我们先从委托的用途讲起。

1.1 委托的用途

在C/C++中,"函数指针"将对方法的引用作为实参传给另一个方法,而委托在C#中承担着相似的功能。虽然这样说,但我们对委托用途的理解可能还是很抽象,这里用一个简单的例子帮助理解。

冒泡排序是最基础的排序算法,它的代码大致如下:

csharp 复制代码
public static void BubbleSort(int[] items) {
    if (items == null) return;
    for(var i = items.Length - 1; i >= 0; i--)
    {
        for(var j = 0; j+1 <= i; j++)
        {
            if (items[j] > items[j + 1])
            {
                var temp = items[j];
                items[j] = items[j + 1];
                items[j + 1] = temp;
            }
        }
    }
}

该方法对整数数组执行升序排序。

但为了能够选择升序和降序,我们开始拓展这段代码。

第一个方案:拷贝以上代码,然后把大于操作符换成小于操作符。

第二个方案:增加一个参数,指出我们当前希望如何排序,然后在代码里进行判断。

但以上代码只照顾到了两种可能的排序方式,如果还想要按其他方式进行排序,代码就会变得很庞大。

为了增加灵活性,减少重复代码,我们可以将比较方法作为参数传入。而此时我们需要有一个数据类型可以表示方法,这就是委托。

委托加入后,代码会变成这样:

csharp 复制代码
public static void BubbleSort(int[] items,Func<int,int,bool> compare) {
    if (items == null) return;
    if (compare == null) return;
    for(var i = items.Length - 1; i >= 0; i--)
    {
        for(var j = 0; j+1 <= i; j++)
        {
            if (compare(items[j], items[j+1]))
            {
                var temp = items[j];
                items[j] = items[j + 1];
                items[j + 1] = temp;
            }
        }
    }
}

显然灵活多了。

PS:写到这里我突然理解了Sort((x,y)=>x>y)的含义,之前对升序到底对应x>y还是y>x总是一知半解。x>y那么x和y就交换,所以就是升序排列(虽然Sort底层是优化过的快排,不是冒泡排序,但也有两个数比对的步骤,所以代入一下就能得出结论)。

1.2 委托的声明

1.2.1 自定义委托

声明委托需要使用关键词delegate,并且指出委托的返回值和所需参数,只有方法的返回值和参数与委托一致,才可以将方法传递给委托。

csharp 复制代码
//先定义委托
delegate void Feedback();
//然后使用new操作符构造委托实例并传入方法
class Program{
	static void Main(string[] args){
		//向委托的构造函数传递静态方法
        Feedback fbStatic = new Feedback(Program.FeedbackToConsole);
        //传递实例方法
        Feedback fbInstance = new Feedback(new Program().FeedbackToFile);
        
        //调用委托的两种不同方式
        fbStatic.Invoke();
        fbInstance();
	}
}

1.2.2 System.Func和System.Action

为了减少定义自己的委托类型的必要,C#推出了一组常规用途的委托。

Func

代表有返回值的方法。

csharp 复制代码
delegate TResult Func<参数,参数...,out TResult>

Action

代表无返回值的方法。

csharp 复制代码
delegate void Action<参数,参数...>

PS:虽然C#开始提供Func和Action委托,减去了自定义委托类型的必要(要写声明还要自定义名字),但有时候出于可读性,还是可以考虑声明自己的委托类型。如Comparer委托就能使人对其用途一目了然。

csharp 复制代码
public delegate bool Comparer(int first,int second);
class Program{
	public static void BubbleSort(int[] items,Comparer comparer){}
}

1.3 委托是一种特殊的类

委托实际上是特殊的类,它派生自System.MulticastDelegate,而后者又派生自System.Delegate,其实把它理解成一种C#里的类型就可以了,但它和一般的类型又有些不同。如果说int,string等是对数据类型的定义,那么委托就类似于对"方法类型"的定义(看着下面的代码仔细琢磨)。

csharp 复制代码
string str;
delegate void Method(int num);  //理解:Method是变量名,delegate/返回值/参数共同构成了一种"方法类型"

2 Lambda表达式

上文提到的冒泡排序,如果我们想去调用它,代码大致如下:

csharp 复制代码
public static void BubbleSort(int[] items,Func<int,int,bool> comparer){}

//声明方法
public static void GreaterThan(int first,int second){
	return first>second;
}

//Main函数里调用
static void Main(string[] arg){
	int[] items = new int[5];
	//...初始化
	BubbleSort(items,GreaterThan);
}

你会发现整个过程下来要进行的准备有点太过复杂了,其实我们只需要主体的return first>second。于是C#提供了匿名函数(C# 3.0叫Lambda表达式),简化了这个过程。

csharp 复制代码
BubbleSort(items,(first,second)=>{return first>second});

3 事件(event)

事件就是对委托的封装,相对于委托,它只提供了"+="和"-="两个方法,保证了在外部操作时的安全性。

对订阅的封装

只提供"+="和"-=",避免程序员在编写代码时错误地使用"="代替"+="。

对发布的封装

不再提供Invoke方法,保证只有指定字段发生变更时,委托方法才会被调用,避免外部主动调用。

csharp 复制代码
private delegate void Method();
public event Method EventName;   //提供给外部
相关推荐
Java Fans2 小时前
C# 中串口读取问题及解决方案
开发语言·c#
盛派网络小助手2 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
码农君莫笑3 小时前
信管通低代码信息管理系统应用平台
linux·数据库·windows·低代码·c#·.net·visual studio
鲤籽鲲4 小时前
C# Random 随机数 全面解析
android·java·c#
神码编程5 小时前
【Unity功能集】TextureShop纹理工坊(五)选区
unity·游戏引擎·shader·ps选区
fkdw6 小时前
C# Newtonsoft.Json 反序列化派生类数据丢失问题
c#·json
浅尝辄止;9 小时前
C# 异步编程
c#
m0_7482517210 小时前
Android webview 打开本地H5项目(Cocos游戏以及Unity游戏)
android·游戏·unity
benben04410 小时前
Unity3D仿星露谷物语开发7之事件创建动画
unity·游戏引擎