C#基础 - 委托(匿名函数、lambda、泛型Action/Func和注意点)

C#基础 - 委托(匿名函数、lambda、泛型Action/Func和注意点)

委托和事件都看了几遍了,感觉还是模模糊糊的,得写个文章总结一下委托和事件。弄明白委托和事件是对C#异步编程、同步、任务的理解的基础。

出现事件的地方,必然有委托出现;而委托则不一定会有事件出现。

1. 委托

委托在C#中定义为一种面向对象形式的方法寻址方案。简单来讲,就是定义一个类型,然后表示这个类型代表某一种方法。所以委托和class同级别,是一种类型,并且是引用类型。
委托可以实现将方法当做一个参数传递给另一个方法。
委托不关心方法叫什么,也不关心方法从哪来(归属于哪个类或者哪个对象),只关心方法需要***哪些参数,返回什么类型***。
记住 : 委托 == 引用类型 == delegate关键字+返回类型+参数
委托的定义形式:

delegate <返回类型>  委托名(参数列表);//参数列表代表任意个参数

委托可以定义在一个类的外部也可以定义在类的内部。
例子:

public delegate double CalculateDelegate(double x, double y);

上述委托声明了一个计算的规范,那么我们来为它赋值(使用了lambda表达式):

CalculateDelegate add= (x, y) => x + y;// 加
CalculateDelegate subtract = (x, y) => x - y;// 减
CalculateDelegate multiple = (x, y) => x * y; //乘
CalculateDelegate devide = (x, y) => x / y; //除

这儿说一下生产环境中委托实例化的命名规则是一般表示 —— 一个表示动作的名词!实例可以为null,使用时也需要做null判断。

调用:

double addResult= add(10, 10);
double subtractResult= subtract(19, 10);
double multipleResult= multiple(10, 5);
double devideResult = devide(10,5);

特别的,C#中委托支持多路广播,所以也可以使用+、-进行注册和删除。多路广播是指在事件和委托中有多个监听器或响应方法,当事件触发或者委托调用的时候,注册的方法组(即委托存储的方法)全部都会被调用。当使用这种方式对委托进行赋值的时候,委托将自动转为方法组(就像变成了委托数组CalculateAreaDelegate[],然而不是),简单理解就是 委托对象内部创建了一个列表,然后把赋值给它的方法都存进去了。

CalculateDelegate calculate = add;// calculate必须先赋值一个方法
calculate += subtract;// 增加 
calculate += multiple; // 增加
calculate -= subtract; // 减去

当用calculate(x,y)运行时,这儿calculate的运行结果只会返回最后一次注册的方法的执行结果,其他的方法执行了,但是方法的执行结果无法用变量接收到。
所以这里有一个很重要的实践,如果有需要把委托当做一个方法列表进行使用的时候,最好声明为void或者抛弃返回值的具体内容。
特别指出:多播委托执行顺序不确定,随着.net framework\core的版本不同,测试电脑的不同,各种执行顺序结果均有差异。所以不可编写依赖特定顺序调用方法的代码!!!
另:如果通过委托调用其中的一个方法抛出异常,整个迭代就会停止。
解决办法:使用Delegate类
Delegate类定义GetInvocationList()方法,返回一个Delegate数组。

Delegate[] dels = calculate.GetInvocationList();
foreach (Calculate itemDel in dels)
{
	try
	{
		itemDel(10,2);
	}
	catch (Exception)
	{
		throw;
	}
}

2. 匿名方法

可以方便快捷的使用委托。是用作委托参数的一段代码,可以方便快捷的使委托实例化。其底层编译还是会编译成一个方法。
不建议再使用匿名方法,C#3.0后使用lambda表达式替代匿名方法。

Calculate calculate = delegate (double x, double y)
{
	return x + y;
};

注意:

  1. 匿名方法内部不能使用跳转语句。
  2. 匿名方法内部不能访问不安全的代码。也不能访问在匿名方法外部使用的ref /out参数。

3. Lambda表达式

语法:

委托类型 del = (参数x, 参数 y) =>
  {
  	//todo 
  };

=>的左边定义参数,右边编写逻辑代码。

  1. 如果委托类型定义了一个参数,则可以免写参数类型,免写参数括号。
public delegate string AddTimeDelegate(string content);
AddTimeDelegate del = s =>
{
	return s + DateTime.Now.ToString();
};

***另外***如果方法块只有一条语句时,可以免写return和方法块花括号(一起免写),编译器会自动生成一个隐式的return语句。

public delegate string AddTimeDelegate(string content);
AddTimeDelegate del = s => s + DateTime.Now.ToString();
  1. 如果委托类型定义了多个参数,需要括号括起来
public delegate string ConnectDelegate(string s1,string s2);
ConnectDelegate del2 = (string s1, string s2) =>
{
	return s1 + s2;
};
Console.WriteLine(del2("hello ", "world!"));

lambda表达式可以访问表达式块外部的变量,称为闭包。访问外部变量的lambda表达式是
Lambda表达式的本质是“匿名方法”,即当编译我们的程序代码时,“编译器”会自动将“Lambda表达式”转换为“匿名方法”。

4. 泛型委托Func和Action

方法的返回类型和签名千千万万,无法对每个方法都去定义对应的委托,.net为了方便使用委托,定义了两个泛型委托。

1. Action

Action<int T,1in T2,…>:表示引用一个void返回类型的方法。至多可以传递16种不同类型的参数。其中,Action可调用没有参数的方法。

2. Func

Func<in T1,in T2,…,out TResult>允许调用带返回类型的方法。至多可以传递16种不同类型的参数和一个返回类型。返回类型放在<>最后一个。其中,Func调用一个带返回类型且无参数的方法。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值