首页 > 代码库 > C# 委托
C# 委托
学习C#的WinForm编程需要涉及到事件(Event),学习事件又要涉及到委托(Delegate)。最开始事件和委托的语法相当困扰我,最后发现委托相当于类型,事件相当于实例。
delegate关键字定义了一个类,派生自System.MulticastDelegate类,而MulticaseDelegate类又派生自System.MulticaseDelegate类。尽管委托类在语法上很奇怪,但是不能否认委托仍然属于面向对象的范畴,委托仍是一个类,只能说明C#增加了快捷使用委托的语法糖。
int x = 40;
GetAString firstStringMethod = new GetAString(x.ToString);
Console.WriteLine("String is {0}", firstStringMethod());
委托的实例是一个方法地址,既可以是实例方法,也可以是静态方法。方法名称后面带圆括号表示方法的调用,不带圆括号表示方法的地址。
关于 委托推断(delegate inference)
委托推断可以说是 C# 2.0 增加的一个语法糖。将一个方法的地址传送给一个委托变量,编译器会自动检测委托变量的委托类型,然后根据委托类型创建委托实例,并把方法地址传送给委托的构造函数。
GetAString firstStringMethod = new GetAString(x.ToString);
GetAString firstStringMethod = x.ToString;
说到这里,委托的调用其实也是一个语法糖。委托的本质还是一个类,实现一个委托的实例也就是实现一个类的实例,只不过委托的实例只能调用经过定义后固定的方法。
GetAString firstStringMethod = new GetAString(x.ToString);
firstStringMethod();
GetAString firstStringMethod = new GetAString(x.ToString);
firstStringMethod.Invoke();
关于 泛型委托 Action<T> 和 Func<T>
先说泛型,C# 2.0 加入的泛型解决了两个问题。
第一个是避免了“根据不同的参数类型,去定义不同的方法签名”,即“泛型是个筐,啥都往里装”。泛型作为一个开放的数据类型,根据方法调用时的实参的值转换成对应的方法签名,这个过程在编译时就可以实现和纠错。即,泛型实现了代码中同一个方法多个入参实现的不同版本,在编译时可以根据需要分成多个入参类型的不同版本,假如是初级的编译实现方法的话。
第二个是避免了值类型和引用类型之间转换时,所需要的装箱(boxing)和拆箱(unboxing)的开销。在我所设想的泛型编译实现的方法中,最简单的办法莫过于根据泛型参数方法的调用,实现此方法在具体参数类型下的不同版本,即通过编译器实现多个需要的版本,减少代码编写的工作量。
说到底,如果泛型没有特别的编译实现原理的话,就是一个帮助开发者减少代码量的语法糖。不指定具体的参数类型,根据 where 关键字限定参数类型应符合的接口,在编译时根据方法的调用情况具体实现。这样看来泛型和隐式类型一样,即减少了开发者的工作,又再编译时指定或推断出了具体的参数类型,C#仍然是类型安全的强类型语言。
那么 Action<T> 和 Func<T> 呢,在减少开发者工作的道路上更近了一步。面向对象语言大多需要类型的声明、类型的实现、类型的调用这三个步骤。委托作为一个类,也需要先定义再使用。那么通过预定义的Action<T>和Func<T>委托,就免去了委托定义的步骤,同时通过泛型的实现,就囊括了常见的大部分委托。委托的定义本来就是指定参数的数量和各自类型,以及返回值的类型,Action<T>和Func<T>一步实现了委托的定义和调用。
delegate double DoubleOp(double x);
static void Main() {
DoubleOp[] operations = {
MathOperations.MultiplyByTwo,
MathOperations.Square
};
}
static void Main() {
Func<double, double>[] operations = {
MathOperations.MultiplyByTwo,
MathOperations.Square
};
}
关于Action<T>和Func<T>还有另一个问题。泛型委托可以实现随用随写,不必提前定义。同时由于没有定义委托名称,各泛型类型之间没有逻辑关联。这样的话使用起来,定义委托相比泛型委托,拥有全局定义的意义,类似于全局变量的意义,只不过由于委托定义了方法的签名,委托一经使用就不可以再更改全局的定义。而泛型委托相比定义委托,调用时即可获知委托变量的方法签名,使用起来更直接,不必查询委托的定义。如果没有性能方面的考虑,当然我也考虑不到,我更愿意使用泛型委托,即Action<T>和Func<T>。
static void ProcessAndDisplayNumber(DoubleOp action, double value) {
double result = action(value);
Console.WriteLine("Value is {0}, result of operation is {1}", value, result);
}
static void ProcessAndDisplayNumber(Func<double, double> action, double value) {
double result = action(value);
Console.WriteLine("Value is {0}, result of operation is {1}", value, result);
}
关于 多播委托
关于多播委托我暂时没啥好说的。
参考资料:《Professional C# 4 and .NET 4》
C# 委托