自定义委托delegate、Action、Function
委托
最初使用委托时,均需要先定义委托类型,然后定义一个符合委托类型签名的函数,在调用前,需声明并创建委托对象,将指定函数与委托进行关联。
public delegate int Math(int param1,int param2); //定义委托类型
Public int Add(int param1,int param2) //定义同签名函数
{
Return param1+param2;
}
Math math; //声明委托
math = new Math(Add); //创建委托对象,与指定进行关联
math(3,4); //调用委托函数
委托只包含一个方法的调用。调用委托的次数和调用方法的次数相同。
如果要包含多个方法,可以使用多播委托(MultiDelegate),或者使用事件订阅多个方法。
委托转换
// delegate1 转 delegate2
var delegate2 = (Delegate2)Delegate.CreateDelegate(typeof(Delegate2), delegate1.Target, delegate1.Method);
委托签名不同的话,无法进行转换,可以换个思路,在内置Lamda委托中调用指定委托。
更多的委托应用,可以参考GenerateDelegateHelper。
EventHandler
EventHandler是一个预定义的委托,表示事件处理程序的方法。
public event EventHandler MyEvent;
public event EventHandler<MyEventArgs> MyEvent;
方法转委托
- 单一属性的委托表达
如果知道属性,比如
public int PropertyName1 {get; set;}
可以直接转换为
var mi = typeof(T).GetProperty("PropertyName1").GetGetMethod();
var methodDelegate = (Func<T, int>)mi.CreateDelegate(typeof(Func<T, int>));
......
var value = methodDelegate(obj);
- 所有属性的委托统一转换
/// <summary>
/// 类型所有属性转换为委托字典
/// </summary>
/// <typeparam name="T">类型</typeparam>
/// <returns></returns>
public static Dictionary<string, Func<T, object>> PropertyToDelegate<T>()
{
var tType = typeof(T);
var properties = tType.GetProperties();
//缓存
var dic = new Dictionary<string, Func<T, object>>(properties.Length);
//对象
var objParameter = Expression.Parameter(tType);
// 创建委托 Func<T, object>
foreach (var pi in properties)
{
// 属性
var expProperty = Expression.Property(objParameter, pi.Name);
// 属性类型统一为object
var expReturnType = Expression.Convert(expProperty, typeof(object));
var propertyDelegateExpression = Expression.Lambda(expReturnType, objParameter);
var lamdaDelegate = (Func<T, object>)propertyDelegateExpression.Compile();
dic.Add(pi.Name, lamdaDelegate);
}
return dic;
}
object obj = lamdaDelegate.Invoke(obj);
事件
事件是委托的实例,只能在方法外部声明在内部调用,普通委托多用于回调,而事件多用于外部接口。
Action 与 Func是.NET类库中增加的内置委托,以便更加简洁方便的使用委托。
Action委托签名不提供返回类型。
public event Action<string, string> MyActionEvent
Func提供返回类型(最后一个参数)。
public event Func<string, string> MyFunctionEvent
委托与事件的不同
比较 | 委托 | 事件 |
---|---|---|
定义 | 委托是一个类,用于封装命名或匿名方法的引用类型, 相当于函数指针,但委托是类型安全和可靠的。 |
事件是特殊类型的多路广播委托。基于委托,可以看作是委托类型的对象。 相当于保存委托的数组。 |
调用 | 可以在类的外部调用。 通过委托的构造函数把方法赋值给委托实例。内部维护着一个字段,指向一个方法。 可以将方法作为参数进行传递。 |
仅可从声明事件的类或结构中调用。 通过+=为事件订阅多个委托实例或方法; 通过-=为事件取消订阅多个委托实例或方法; 当发行者引发该事件时,会调用其事件处理程序方法。 |
应用 | 委托一般用于回调。 触发委托都2种方式:委托实例.Invoke(参数列表),委托实例(参数列表)。 EventHandler就是一个委托。 |
事件一般用于外部接口。事件的处理方法在对象外部订阅,而实际的执行在对象的内部。 |
委托可以把多个方法链接在一起,在事件触发时可同时启动多个事件处理程序。 | 事件不能在外部调用,但可以订阅,封装性更好。 | |
委托与事件:
class A{
public delegate void TestDelegate(string arg1, string arg2);
public event TestDelegate TestEvent;
......
public void Method(){
// 在B中操作
TestEvent("arg1","arg2");
}
}
class B{
A a = new A();
a.TestEvent += B_TestEvent;
......
B_TestEvent?.Invoke(string arg1, string arg2){
// TODO 响应更新A内的操作
}
}
Delegate至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型。这个是祖宗。
Func可以接受0个至16个传入参数,必须具有返回值。
Action可以接受0个至16个传入参数,无返回值。
Predicate只能接受一个传入参数,返回值为bool类型。
有明确的同级或上下级关系时,可使用委托事件;EventHandler优选,与系统的事件设计模式保持一致;类型实例若不频繁创建、销毁,其内的事件,为方便使用,可选择Action、Func。
无明确的拥有者时,为实现响应者的响应事件,可使用路由事件(WPF)或SendMessage(WinForm)方法。