美文网首页
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