委托
委托类型是一种类是一个引用类型的数据结构,委托类型的实例可以存储一个或多个方法的引用,或者说是封装一个或多个方法,通过委托实例,可以间接的调用这些封装的方法,但是所存储的方法的参数列表与返回值类型要与对应的委托类型相同,即类型兼容
委托的缺点
人为失误造成重置
委托很容易因为人为失误造成委托内部封装的所有方法重置,例如
myDelegate +=Method1;
myDelegate +=Method2;
myDelegate =Method3;//此处因为认为失误 将+=写成=,造成委托内部封装的所有方法重置
内存泄漏
委托会引用一个或多个方法,如果一个方法是实例的方法,则一个委托引用这个方法时,这个方法所属的对象必须在内存中是存在的,这个对象的内存一旦被释放,委托就不能间接调用该对象的方法
Action委托与Func委托
首先可以知道自定义委托类型,通过关键字delegate进行声明,例如
public delegate string MyDelegate();
而C#的类库已经为我们准备好了两种委托,即Action委托和Func委托(两者都为泛型委托),许多时候我们并不需要使用自定义委托类型,而是直接使用已经定义好的泛型的Action委托和Func委托
不同点
Action委托无返回值,而Func委托有返回值,两者都有参数列表
委托的幕后
首先要明确的一点是,委托是一个易入手难精通的东西,易入手的地方在于你只需要用delegate关键字定义委托,用new操作符构造委托实例,然后进行熟悉的调用,难精通就在于,编译器和CLR在幕后做了大量的工作来隐藏其复杂性 我们向如下方式定义了一个委托类型:
internal delegate void Feedback(Int32 value);
以上的定义十分简单,但实际上,编译器编译器在幕后会像如下方式定义一个类
internal class Feedback : System.MulticastDelegate {
public Feedback(Object @object, IntPtr method);
public virtual void Invoke(Int32 value);
public virtual IAsyncResult BeginInvoke(Int32 value, AsyncCallback callback, Object @object);
public virtual void EndInvoke(IAsyncResult result);
}
编译器定义的类有四个公共的方法,构造器,Invoke,BeginInvoke,EndInvoke,其中BeginInvoke和EndInvoke方法实现对回调方法的异步问题
事件
首先需要明确很重要的一点:事件不是委托实例!事件不是委托实例!事件不是委托实例! 事件是基于委托的,是一种类型成员,他只能使用+=或者-=而不能直接使用=,弥补了委托种因为人为失误造成的重置问题 事件与属性类似,属性是字段的包装器,而事件是委托类型字段的包装器,作用是隐藏委托实例的大部分内容,仅仅暴露了添加和移除事件处理器的功能(所以只能使用+=或者-=而不能直接使用=),从而保护委托类型的字段不被外界滥用
事件的两种声明方式
public delegate void FooEventHandler(object sender,EventArgs e);//为OnFoo事件声明委托
完整
private FooEventHandler fooEventHandler;//声明委托字段,用来存储事件处理器
public event FooEventHandler OnFoo{
add{
fooEventHandler += value;
}
remove{
fooEventHandler -= value;
}
}
简略
事件的简略声明格式会让事件看起来像一个用event修饰的委托类型的字段(FIELD-LIKE),但并不是一个委托类型的字段!!!并不是一个委托类型的字段!!!并不是一个委托类型的字段!!!
public event FooEventHandler OnFoo;
在简化的事件的声明格式中,事件所包装的委托类型的字段由编译器自动生成,这样一来,表面上似乎能调用一个事件,但为了调用事件处理程序,实际做的事情是调用存储在字段中的委托实例
事件模型
- 事件的拥有者
- 事件
- 事件的响应者(订阅者)
- 事件的处理器 即受到约束的方法,方法的参数列表与返回值需要和事件的委托类型一致
- 事件订阅与注销
面试问题
委托和事件的关系
首先需要明白的一点是,事件不是一个委托类型的字段,更不是一种特殊的委托类型的字段 委托与事件的关系和字段和属性的关系类似,属性是字段的包装器,而事件是委托类型字段的包装器 在使用了简化的事件声明格式后,会让其看起来像一个用event来修饰的委托类型的字段,但是实际上在编译时,事件所包装的委托类型的字段由编译器自动生成
为什么要使用委托类型来声明事件(为什么事件是基于委托的)
隐藏委托实例的大部分内容,仅仅暴露了添加和移除事件处理器的功能(所以只能使用+=或者-=而不能直接使用=),从而保护委托类型的字段不被外界滥用 从事件拥有者的角度来讲:是为了表明事件拥有者能对外传递哪些消息 从事件订阅者的角度来讲:委托是一种约定,约束了能够使用什么样的签名的方法里响应事件 委托类型的实例讲用于存储事件处理器