自定义委托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;

方法转委托

  1. 单一属性的委托表达
    如果知道属性,比如
    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);
  1. 所有属性的委托统一转换
/// <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)方法。

posted @ 2020-01-10 18:17  wesson2019  阅读(298)  评论(0编辑  收藏  举报