欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > 【C#】委托/Lambda/事件

【C#】委托/Lambda/事件

2025/9/26 19:04:04 来源:https://blog.csdn.net/manpi/article/details/141110646  浏览:    关键词:【C#】委托/Lambda/事件

目录

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

0 参考文章

知乎:事件是以特殊方式声明的委托字段吗
书籍:《C# 7.0本质论》

1 委托(delegate)

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

1.1 委托的用途

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

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

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;}}}
}

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

但为了能够选择升序和降序,我们开始拓展这段代码。
第一个方案:拷贝以上代码,然后把大于操作符换成小于操作符。
第二个方案:增加一个参数,指出我们当前希望如何排序,然后在代码里进行判断。

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

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

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

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,并且指出委托的返回值和所需参数,只有方法的返回值和参数与委托一致,才可以将方法传递给委托。

//先定义委托
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
代表有返回值的方法。

delegate TResult Func<参数,参数...,out TResult>

Action
代表无返回值的方法。

delegate void Action<参数,参数...>

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

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等是对数据类型的定义,那么委托就类似于对“方法类型”的定义(看着下面的代码仔细琢磨)。

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

2 Lambda表达式

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

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表达式),简化了这个过程。

BubbleSort(items,(first,second)=>{return first>second});

3 事件(event)

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

对订阅的封装
只提供“+=”和“-=”,避免程序员在编写代码时错误地使用“=”代替“+=”。

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

private delegate void Method();
public event Method EventName;   //提供给外部

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词