委托,是一种特殊的类,它是将调用时操作对象和指定方法的封装。委托的构造函数,需两个参数target,methodPtr分别用于指定操作的对象和方法。委托的调用通过Invoke方法,方法的原型及委托定义一致。委托同时提供了BeginInvoke及EndInvoke方法用于支持异步。
委托链的形成利用Delegate的基类MulticastDelegate的_prev字段,[关于Delegate与MultiCastDelegate],使用Combine和Remove方法操作委托链。
防止委托链中的由于单个委托出现异常或占阻塞,可利用MulticastDelegate的GetInvocationList()获取委托链中委托克隆体形成委托数组,用于实现对其的更多控制。
如果在不明确委托对应方法原型的时候使用委托。需要用到Delegate.CreateDelegate方法。
public class Delegate { public static Delegate CreateDelegate(Type delType, System.Reflection.MemberInfo mi); public static Delegate CreateDelegate(Type delType, Type type, string methodName); public static Delegate CreateDelegate(Type delegateType, object obj, String methodName); public static Delegate CreateDelegate(Type delegateType, object obj, string methodName, Boolean ignoreCase); //...... public object DynamicInvoke(object[] args); }
Demo:(通过指定生成委托对象的参数实现在未知方法原型时的调用)
namespace DelegateDemo { delegate object TwoInt32(int n1, int n2); delegate object OneString(string s1); class Program { static void Main(string[] args) { Delegate d = Delegate.CreateDelegate(typeof(TwoInt32), typeof(Program), "Add"); object result = d.DynamicInvoke(1, 2); Console.WriteLine(result); d = Delegate.CreateDelegate(typeof(OneString), typeof(Program), "NumChars"); result = d.DynamicInvoke("hello cnblogs!"); Console.WriteLine(result); } static object Add(int n1, int n2) { return n1 + n2; } static object NumChars(string s1) { return s1.Length; } }
事件的定义:
public event EventHandler MyEvent;
事件的作用是通知侦听事件的用户执行其自定义的操作。关于事件的侦听与注销,实则是对委托链的操作,编译器会将事件转化成一个委托字段及两个方法(add_*,remove_*用于操作委托字段)。 事件,我理解的是将有效信息包装到继承到EventArgs对象中,触发事件,通知事件侦听者作其定义的反应。事件的必须具备的三个能力:对象登记事件;对象注销事件;定义事件的对象维护登记对象的集合并通知。这三项功能从实现角度看,委托链都能做到,而事件是对委托链做了一层包装。博客园中有博友曾提过“事件与委托的关系好比字段与属性的关系。”这可以从显式控制事件注册的实现中看到:
显式控制事件注册:
public delegate void SampleEventHandler(object sender,EventArgs args); private SampleEventHandler sampleEventHandlerDelegate; public SampleEventHandler Sample { add { sampleEventHandlerDelegate = (SampleEventHandler)Delegate.Combine(sampleEventHandlerDelegate,value); } remove { sampleEventHandlerDelegate=(SampleEventHandler)Delegate.Remove(sampleEventHandlerDelegate,value); } }
从显式控制事件注册的代码可以很清晰的看出,是通过声明一个委托字段,然后定义其访问器来实现的。FCL中System.Windows.Forms.Control一个类中定义了近60个事件,也是通过这种方式实现用于减少内存的浪费。基本思想:让每个对象保存一个事件/委托对的集合,当新对象被构建时,集合为空;当事件被登记时,如果集合中存在,则将委托实例给合到委托链上;若不存在,则将新事件及实例添加到集合。