C# 委托 02
2019-08-22 本文已影响0人
JeetChan
声明
本文内容来自微软 MVP solenovex 的视频教程——真会C#? - 第4章 委托、事件、Lambda表达式(完结),大致和第 2 课—— 4.1 委托(下) 对应。可在 GitHub 中查看 C# 视频教程的配套PPT
本文主要包括以下内容:
- 实例方法目标和静态方法目标
- 泛型委托类型
- Func 和 Action 委托
- 委托 vs 接口
- 委托的兼容性
实例方法目标和静态方法目标
当一个实例方法被赋值给委托对象的时候,这个委托对象不仅要保留着对方法的引用,还要保留着方法所属实例的引用。System.Delegate
的Target
属性就代表着这个实例。如果引用的是静态方法,那么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
- Covariance,out
- ContraVariance,in

参考
Delegates (C# Programming Guide)
Delegate Class
MulticastDelegate Class