C#学习笔记—委托、事件和Lambda
1、在.NET Framework里,回调仍是可能的,它们的功能是由使用更为安全和面向对象的委托(delegate)来完成的。本质上来讲,委托是一个类型安全的对象,它指向程序中另一个以后会被调用的方法(或多个方法)。委托类型包含3个重要的信息:它所调用的方法的名称;该方法的参数(可选);该方法的返回值(可选)。
2、C#委托类型定义会生成一个密封类,它含有3个编译器生成的方法,这3个方法的参数与返回值基于委托声明。同时在使用C#中delegate关键字创建委托的时候,也就间接声明了一个派生自System.MulticastDelegate的类,这个类时期继承类可以访问包含由委托对象维护的方法地址的列表以及一些处理调用列表的附加方法。
3、如果要将目标对象方法插入指定委托对象,只要向委托的构造函数传入方法名称即可。
1 namespace SimpleDelegate
2 {
3 //这个委托可以指向任何传入两个整数并返回一个整数的方法
4 public delegate int BinaryOp(int x, int y);
5
6 //这个类包含了BinaryOp将指向的方法
7 public class SimpleMath
8 {
9 public static int Add(int x, int y) { return x + y; }
10 public static int Subract(int x, int y) { return x - y; }
11 }
12 class Program
13 {
14 static void Main(string[] args)
15 {
16 //创建一个指向Simple.Add()方法的BinaryOp对象
17 BinaryOp b = new BinaryOp(SimpleMath.Add);
18
19 //使用委托对象间接调用Add()方法
20 Console.WriteLine("10 + 10 is {0}", b(10,10));
21 Console.ReadLine();
22 }
23 }
24 }
System.MulticastDelegate/System.Delegate部分成员:
Method:此属性返回System.Reflection.MethodInfo对象,用以表示委托维护的静态方法的详细信息;
Target:此属性返回表示委托维护的方法的对象。如果返回值为null,调用的方法是一个静态成员;
Combine():此静态方法给委托维护的列表添加一个方法。
GetInvocationList():此方法返回一个System.Delegate类型数组,其中数组中的每个元素都表示一个可调用的特定方法;
Remove()&RemoveAll():这些静态方法从调用列表中移除一个(或所有)方法。
static void DisplayDelegateInfo(Delegate delObj)
{
//输出委托调用列表中每个成员的名称
foreach (Delegate d in delObj.GetInvocationList())
{
Console.WriteLine("Method Name: {0}", d.Method);
Console.WriteLine("Type Name: {0}", d.Target);
}
}
//.Net委托也可以指向实例方法
SimpleMath m = new SimpleMath();
BinaryOp b = new BinaryOp(m.Add);
//显示这个对象的信息
DisplayDelegateInfo(b);
4、使用委托发送对象状态通知
1 //定义委托类型
2 public delegate void CarEngineHandler(string msgForCaller);
3
4 //定义每个委托类型的成员变量
5 private CarEngineHandler listOfHandlers;
6
7 //向调用者添加注册函数
8 public void RegisterWithCarEngine(CarEngineHandler methodToCall)
9 {
10 listOfHandlers = methodToCall;
11 }
5、.Net委托内置支持多路广播。换句话说,一个委托对象可以维护一个可调用方法的列表而不只是单独一个方法。给一个委托对象添加多个方法是,不用直接分配,重载+=操作符即可。
Delegate类还定义了一个静态Remove()方法,允许调用者动态地从委托对象的调用列表中移除方法,可以使用-=操作符作为简写方式。
6、C#提供了一种叫做方法组转换的简便方法。该特性允许我们在调用以委托作为参数的方法时直接提供方法的名称,而不用创建委托对象。
7、委托协变:协变允许我们构建一个委托,能指向返回类及相关继承体系的方法。为了得到派生类型的成员,仅仅做个显式强制类型转换即可。
8、泛型委托
9、C#事件
为了简化自定义方法的构建来为委托调用列表增加和删除方法,C#提供了event关键字。在编译器处理event关键字的时候,它会自动提供注册和注销方法以及委托类型任何必要的成员变量。这些委托成员变量总是声明为私有的,因此不能直接从触发事件的对象访问它们。
定义一个事件分为两个步骤。首先,我们需要定义一个委托类型,它包含在事件触发时将要调用的方法。其次,通过C#event关键字用相关委托声明这个事件。
1 //定义委托类型,这个委托用来与Car事件协作
2 public delegate void CarEngineHandler(string msg);
3
4 //这种汽车可以发送这些事件
5 public event CarEngineHandler Exploded;
6 public event CarEngineHandler AboutToBlow;
为确保调用者注册事件,需要在调用委托的方法之前检查这个事件是否是无效值。
10、C#事件事实上会扩展为两个隐藏的公共方法,一个带add_前缀,另一个带remove_前缀。C#事件也简化了注册调用者事件处理程序的操作。现在无需指定自定义辅助方法,调用者仅需使用+=和-=操作符即可。注册一个事件要遵循以下模式:
1 Car c1 = new Car("SlugBug", 100, 10);
2
3 //注册事件处理程序
4 c1.AboutToBlow += new Car.CarEngineHandler(CarIsAlmostDoomed);
5 c1.AboutToBlow += new Car.CarEngineHandler(CarAboutToBlow);
6
7 Car.CarEngineHandler d = new Car.CarEngineHandler(CarExploded);
8 c1.Exploded += d;
11、C#匿名方法在事件注册时直接将一个委托与一段代码相关联,这种代码的正式名称为匿名方法。匿名方法将在调用者使用+=语法处理事件时被内联定义。严格来讲,我们不需要接受由指定时间发送的传入参数,但如果想使用可能传入的参数,需要通过委托类型指定参数原型。
匿名方法非常有趣,它使我们能访问定义它们的方法的本地变量。这些变量成为匿名方法的外部变量。
匿名方法不能访问定义方法中的ref或out参数。
匿名方法中的本地变量不能与外部方法中的本地变量重名。
匿名方法可以访问外部类作用域中的实例变量(或静态变量)。
匿名方法内的本地变量可以与外部类的成员变量同名。
12、Lambda表达式:只是用更简单的方式来写匿名方法,彻底简化了对.Net委托类型的使用。