C# 委托 02

2019-08-22  本文已影响0人  JeetChan

声明

本文内容来自微软 MVP solenovex 的视频教程——真会C#? - 第4章 委托、事件、Lambda表达式(完结),大致和第 2 课—— 4.1 委托(下) 对应。可在 GitHub 中查看 C# 视频教程的配套PPT

本文主要包括以下内容:

  1. 实例方法目标和静态方法目标
  2. 泛型委托类型
  3. Func 和 Action 委托
  4. 委托 vs 接口
  5. 委托的兼容性

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

当一个实例方法被赋值给委托对象的时候,这个委托对象不仅要保留着对方法的引用,还要保留着方法所属实例的引用。System.DelegateTarget属性就代表着这个实例。如果引用的是静态方法,那么Target属性的值就是null

public delegate void ProgressReporter (int percentComplete);

class Test
{
    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)
    }
}

class X
{
    public void InstanceProgress (int percentComplete)
    => Console.WriteLine (percentComplete);
}

泛型委托类型

委托类型可以包含泛型类型参数。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); // Hook in 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 arg2);
//... and so on, up to T16

delegate void Action ();
delegate void Action <in T> (T arg);
delegate void Action <in T1, in T2> (T1 arg1, T2 arg2);
// ... and so on, up to T16

public static void Transform<T> (T[] values, Func<T,T> transformer)
{
    for (int i = 0; i < values.Length; i++)
        values[i] = transformer (values[i]);
}

委托 vs 接口

委托可以解决的问题,接口都可以解决。

什么情况下更适合使用委托而不是接口呢?当下列条件之一满足时:

class Squarer : ITransformer
{
    public int Transform (int x) => x * x;
}

class Cuber : ITransformer
{
    public int Transform (int x) => x * x * x;
}

// ...

static void Main()
{
    int[] values = { 1, 2, 3 };
    Util.TransformAll (values, new Cuber());
    foreach (int i in values)
        Console.WriteLine (i);
}

委托的兼容性

委托类型

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

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)可以比方法的参数(parameter)定义更具体。委托可以接受比它的方法目标更具体的参数类型,这个叫ContraVariance。和泛型类型参数一样,委托的variance仅支持引用转换。

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
}

返回类型

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

delegate object ObjectRetriever();

class Test
{
    static void Main()
    {
        ObjectRetriever o = new ObjectRetriever (RetrieveString);
        object result = o();
        Console.WriteLine (result); // hello
    }
    
static string RetrieveString() => "hello";
}

泛型委托类型参数的variance

C# 委托

参考

Delegates (C# Programming Guide)
Delegate Class
MulticastDelegate Class

上一篇 下一篇

猜你喜欢

热点阅读