委托 delegate
可以认为委托是一个持有一或多个方法的对象,执行委托,会执行它所“持有”的所有方法。
所有的委托类型都派生于System.MulticastDelegate,而它又派生于System.Delegate。
可以把委托看成一个包含有序方法列表的对象。
声明委托类型
不需要在类内部声明,因为它是一种类型声明。
在创建委托类型变量时,可使用new
也可不使用,因为在方法名称和其相应委托类型之间存在隐式转换。
创建委托对象
//声明委托类型的变量
MyDel md;
//两个等价的创建语法
md = new MyDel(Function1);
md = Function1;
//使用匿名方法
md = delegate(int x){ System.Console.WriteLine(x);}
委托的组合——多播
使用“+”、“+=”、“-=”可以组合多个委托或为委托添加(删除)方法。
注:委托是引用类型(不可变),所以实际上上示运算结果是变量指向一个新的委托。
移除方法时,将从列表最后开始搜索,并移除第一个匹配的实例。
删除不存在的方法没有效果。
调用委托
调用空委托会抛出异常。
调用委托时传入的参数会用于调用列表中的每一个方法(除非有输出参数)
委托有返回值的情况
返回调用列表中最后一个方法的返回值。
带引用参数的委托
调用列表中每个方法执行完毕之后,下一个方法的参数会传入上一个方法修改过的新值。
匿名方法和lambda表达式
若匿名方法不使用任何参数,且委托的参数列表没有输出参数(out),可以使用空圆括号或省略圆括号方式简化匿名方法的参数列表。——调用委托时依然要传入参数。
delegate void MyDel(int x);
MyDel md;
md = delegate /*无参省略圆括号*/ {
//statement
};
md(i); //不传参会报错
若委托声明包含数组参数(params),则匿名方法参数列表将忽略params关键字
delegate void ParamsDel(int x, params int[] Y);
ParamsDel pd = delegate (int x, /*省略params*/ int[]Y){……};
捕获外部变量
在匿名方法外部定义的变量,可以在匿名方法中使用,称为捕获(capture)。
在匿名方法内修改变量的值时,外部变量也会被修改。
delegate void OuterDel();
int x = 0;
OuterDel od = delegate {
x += 2;
Console.WriteLine("In anonymous function x is: {0}", x);
}
od(); // x = 2
Console.WriteLine("After all statements, x becomes: {0}", x); // x = 2
只要捕获方法还是委托的一部分,就算变量已经离开了作用域,捕获的外部变量也会一直有效。
lambda表达式
MyDel del = delegate(int x){return x + 1;}; // 匿名函数
Medel del1 = (int x) => {return x + 1;}; //lambda表达式,省略delegate关键字
Medel del2 = (x) => {return x + 1;}; //lambda表达式,进一步省略类型
Medel del3 = x => {return x + 1;}; //lambda表达式,只有一个隐式参数,省略圆括号
Medel del4 = x => x + 1; //lambda表达式,进一步省略return
除非有ref或out参数,否则可以省略参数类型。
没有参数必须使用空括号。
事件 event
发布者订阅者模式
事件
声明事件
事件需声明在一个类(发布者)内;
需要委托类型,任何附加到事件(如注册)的处理程序都必须与委托类型的签名和返回类型匹配。
public;
不能使用new来创建对象。
多个事件名使用逗号分隔,可在一条语句内声明多个事件。
事件是成员,不能在一段可执行代码中声明。事件成员隐式自动初始化为null。
订阅事件
使用+=
(-=
)运算符为事件增加(删除)处理程序。
CLassA.EventA += FuncA; //为事件注册实例方法
ClassA.EventA += ClassB.FuncB; //为事件注册静态方法
ClassA.EventA += new EventHandler(cc.FuncC); //委托形式
ClassA.EventA += () => a++; //lambda表达式
ClassA.EventA += delegate{a++;}; //匿名函数
触发事件
触发事件的语法和调用方法一样:
标准事件的用法
事件使用的标准模式的根本是System命名空间声明的EventHandler
委托类型。
public delegate void EventHandler(object sender, EventArgs e);
第一个参数(sender)用来保存触发事件的对象的引用;
第二个参数(EventArgs)用来保存状态信息,指明声明类型适用于该程序。EventArgs设计为不能传递热门和数据,若想传递数据,则需要自定义一个派生自EventArgs的类(类名以EventArgs结尾),保存需要的数据。
通过扩展EventArgs来传递数据
可以通过泛型委托声明事件。
//自定义类扩展EventArgs
public class MyEventArgs : EventArgs{
public int MyData{ get; set; }; //存储需要的数据
}
//通过泛型委托获取事件
public event EventHandler<MyEventArgs> MyEvent;
事件访问器
add和remove访问器,声明类似类的属性。
声明了事件访问器之后,事件不包含任何内嵌委托对象,必须实现自己的机制来存储和移除事件注册的方法。
访问器内不能包含return语句。