上一篇:C#委托(上)
✦ 当一个实例方法被赋值给委托对象的时候,这个委托对象不仅要保留着对方法的引用,还要保留着方法所属实例的引用。
✦ System. Delegate的Target属性就代表着这个实例。
✦ 如果引用的是静态方法,那么Target属性的值就是null。
举例:
public delegate void ProgressReporter(int percentComplete);
class X
{
public void InstanceProgress(int percentComplete)
=> Console.WriteLine(percentComplete);
}
public class Program
{
static void Main(string[] args)
{
X x = new X();
ProgressReporter p = x.InstanceProgress;
p(99);
Console.WriteLine(p.Target == x); //True --对实例的引用
Console.WriteLine(p.Method); //Void InstanceProgress(Int32) --对方法的引用
Console.ReadKey();
}
}
输出结果:
99
True
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 Program
{
static int Square(int x) => x * x;
static void Main(string[] args)
{
int[] values = { 1, 2, 3 };
Util.Transform(values, Square); //Hook in Square
foreach (int i in values)
Console.Write(i + " "); //1 4 9
Console.ReadKey();
}
}
简写:
Util.Transform(values, Square);
Util.Transform<int>(values, Square);
输出:
1 4 9
使用泛型委托,就可以写出这样一组委托类型——它们可调用的方法可以拥有任意的返回类型和任意(合理)数量的参数。
委托结合事件是能够改变项目框架的东西。
Func和Action的源码定义:
举例:
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]);
}
}
class Program
{
static int Square(int x) => x * x;
static void Main(string[] args)
{
int[] values = { 1, 2, 3 };
Util.Transform(values, Square); //Hook in Square
foreach (int i in values)
Console.Write(i + " "); //1 4 9
Console.ReadKey();
}
}
输出:
1 4 9
声明委托的这句必要性是,
以后用别的方法的时候,能调用(所声明的同类型的)方法,
那这样的话,甚至可以简写。
简写:
public delegate T Transformer<T>(T arg);
Transformer<T> t
To:
Func<T,T> t
委托vs接口
委托可以解决的问题,接口都可以解决。
什么情况下更适合使用委托而不是接口呢?当下列条件之一满足时:
✦ 接口只能定义一个方法
✦ 需要多播能力
✦ 订阅者需要多次实现接口
委托的兼容性-委托类型
✦ 委托类型之间互不相容,即使方法签名一样:
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仅支持引用转换。
引用类型:基类为Objcet
值类型:均隐式派生自System.ValueType:
值类型: | byte,short,int,long,float,double,decimal,char,bool 和 struct 统称为值类型。 |
引用类型: | string 和 class统称为引用类型。 |
举个例子:
以上述图片为例:
object可以具体为string;
long不能具体为int,因为发生了数值转换。
object也不可以具体为int,因为发生了装箱操作。
所以注意:
委托的协变或逆变仅支持引用转换。
这个和泛型的类型参数是一样的。
委托的兼容性-返回类型
✦调用方法时,你可以得到一个比请求的类型更具体的类型的返回结果。
✦委托的目标方法可以返回比委托描述更具体地类型的返回结果,Covariance。
Covariance——协变(协方差)。
泛型委托类型参数的variance
Covariance,out
ContraVariance,in
参数只作为输出就用out来修饰,只作为输入就用in。