美文网首页
Delegates, events, lambda expres

Delegates, events, lambda expres

作者: 柒轩轩轩轩 | 来源:发表于2019-11-01 09:24 被阅读0次

    Delegates 委托

    委托是一个对象,它知道如何调用一个方法

    委托类型和委托实例

    • 委托类型定义了委托实例可以调用的那类方法,具体来说,委托类型定义了方法的返回类型和参数
    delegate int Transformer (int x); //define argument type is int, and return type is int 委托类型
    static int Square (int x) {return x *x} // 上面的委托类型可以调用这个方法,因为类型一致
    
    • 把方法赋值给委托变量的时候就创建了委托实例
      Transformer t = Square;
    • 调用
      int answer = t(3); // answer is 9
    public class Program
    {
      delegate int Transformer(int x);
      static int Square(int x) => x * x;
      static void Main ()
      {
        Transformer t = Square; //Create delegate instance
        int result = t(3); //Invoke delegate 
        Console.WriteLine(result);
      }
    static int Square (int x) => x *x;
    }
    

    委托实例

    • 委托的实例实例其实就是调用者的委托: 调用者调用委托,然后委托调用目标方法
    • 间接地把调用者和目标方法解耦合了
    • 简写 Transformer t = Square; ==> Transformer t = new Transformer (Square);
    • 简写 t(3) ==> t.Invoke(3)

    编写插件式的方法

    • 方法是在运行时才赋值给委托变量的
    public delegate int Transformer(int x);
    class Util
    {
      public static void Transform(int[] values, Transformer t)
      {
        for (int i = 0; i < values.Length; i ++)
          {
            values[i] = t(values[i]);
          }
      }
    }
    
    public class Program 
    {
      static int Square(int x) => x*x;
    
      static void Main()
      {
        int[] values = {1,2,3};
        Util.Transform(values, Square);
        foreach (int i in values)
        {
          Console.WriteLine($"{i}");
        }
      }
    }
    

    多播委托

    所有的委托实例都具有多播的能力,一个委托实例可以引用一组目标方法

      • 和+= 操作符可以合并委托实例
        SomeDelegate d = SomeMethod1 ;
        d += SomeMethod2;
    • 调用d就会调用SomeMethod1 和 SomeMethod2

    • 委托的调用顺序与他们的定义顺序一致

    • -和 -=会把右边的委托从左边的委托里移除
      d -= SomeMethod

    • 委托变量使用+ 或者 += 操作符时,其操作数可以是null,就相当于把一个新的赋值给了委托变量
      SomeDelegate d = null;
      d += SomeMethod1;
      相当于
      d = SomeMethod1

    • 对单个目标方法的委托变量使用 -=操作符时,就相当于把null值赋给了委托变量

    • 委托是不可变的

    • 使用+=或-=操作符时,实际上是创建了新的委托实例, 并把它赋给当前的委托变量

    • 如果多播委托的返回类型不是void,那么调用者从最后一个被调用的方法来接收返回值。前面的方法仍然会被调用,但是其返回值就被弃用了

    实例方法目标和静态方法目标

    • 当一个实例方法被赋值给委托对象的时候,这个委托对象不仅要保留着对方法的引用,还有保留着方法所属实例的引用
    • System.Delegate 的Target属性就代表着这个实例
    • 如果引用的是静态方法,那么Target属性的值就是null
    public delegate void ProgressReporter (int percentComplete)
    class X
    {
      public void InstanceProgress (int percentComplete) 
      => Console.WriteLine(percentComplete);
    }
    class Program
    {
      static void Main()
      {
        X x = new X();
        ProgressReporter p = x.InstanceProgress;
        p(99); //99
        Console.WriteLine(p.Target == x); //True  
        Console.WriteLine(p.Method); //Void InstanceProgress(int32);
      }
    }
    

    泛型委托类型

    • 委托类型可以包含泛型类型参数
    public delegate T Transformer<T>(T arg);
    
    public class Util
    {
      public static void Transform<T>(T[] values, Transformer<T> t)
      {
        for (int i = 0; i < values.Length; i++)
        values[i] = t(values[i]);
      }
    }
    class Test 
    {
      static void Main()
      {
        int [] values = {1,2,3};
        Util.Transform(values, Square);
        foreach (int i in values)
            Console.Write(i + " "); //1 4 9
      }
      static int Square(int x) => x* x;
    }
    

    Func 和 Action委托

    使用泛型委托,就可以写出这样一组委托类型,他们可调用的方法可以拥有任意的返回类型和任意(合理)数量的参数

    delegate TResult Func <out TResult> ();
    delegate TResult Func <in T, out TResult> (t arg);
    delegate TResult Func <in T1, in T2, out TResult> (T1 arg1, T2 args)
    
    delegate void Action ();
    delegate void Action <in T> (T arg);
    delegate void Action <in T1, in T2> (T1 arg1, T2 arg2);
    
    public class Util
    {
      public static void Transform<T>(T[] values, Func<T,T> t)
      {
        for (int i = 0; i < values.Length; i++)
        values[i] = t(values[i]);
      }
    }
    

    委托 vs 接口

    • 委托可以解决的问题, 接口都可以解决
    • 什么情况下更适合使用委托而不是接口呢,当下列条件之一满足时:
      1 接口只能定义一个方法
      2 需要多播能力
      3 订阅者需要多次实现接口

    委托的兼容性 - 委托类型

    委托类型之间互不相容,即使方法签名一样:

    delegate void D1();
    delegate void D2();
    
    D1 d1 = Method1;
    D2 d2 = d1; //Compile-time error
    

    委托的兼容性 - 委托实例

    如果委托实例拥有相同的方法目标,那么委托实例就认为是相等的

    delegate void D();
    ...
    D d1 = Method1;
    D d2 = Method1;
    Console.WriteLine(d1 == d2); //True
    

    委托的兼容性 - 参数

    • 当你调用一个方法时, 你提供的参数(argument)可以比方法的参数定义更具体
    • 委托可以接受比它的方法目标更具体的参数类型,这个叫ContraVariance
    delegate void StringAction (string s);
    class Test
    {
      static void Main()
      {
      StringAction sa = new StringAction(ActOnObject);
      sa("hello");
      }
      static void ActOnObject (object o) => Console.WriteLine(o); //hello
    }
    

    委托的兼容性- 返回类型

    • 调用方法时,你可以得到一个比请求的类型更具体的返回类型
    • 委托的目标方法可以返回比委托描述里更具体的类型的fan hu

    相关文章

      网友评论

          本文标题:Delegates, events, lambda expres

          本文链接:https://www.haomeiwen.com/subject/pzvnvctx.html