[关闭]
@kuier1992 2015-08-31T09:16:41.000000Z 字数 2848 阅读 2223

C# 委托学习

C#


买来《CLR Via C#》这本书很久了,一直也没有对其进行总结,看的非常凌乱,趁此机会好好总结一下,也算对C#学习的一个总结。

  • 初识委托
  • 用委托回调方法
  • 泛型委托
  • 简化语法

初识委托

委托类型 (delegate type) 表示对具有特定参数列表和返回类型的方法的引用(个人觉得这句话对委托的解释非常好)。通过委托,我们能够将方法作为实体赋值给变量和作为参数传递。委托类似于在其他某些语言中的函数指针的概念。.NET Framework通过委托来提供回调函数机制,委托确保了回调函数是类型安全的。
调用一个委托的方法:

  1. 声明一个委托类型
  2. 声明一个方法包含要执行的代码
  3. 创建一个委托实例
  4. 调用这个委托实例

用委托回调方法

  1. //1、声明一个委托实例
  2. internal sealed class DelegateIntro {
  3. internal delegate void Feedback(Int32 value);
  4. private static void StaticDelegateDemo() {
  5. Console.WriteLine("----- Static Delegate Demo -----");
  6. //传递的为NUll,处理每个数据项都不调用回调方法
  7. Counter(1, 3, null);
  8. //3、创建静态的委托实例,用委托回调静态方法
  9. Counter(1, 3, new Feedback(DelegateIntro.FeedbackToConsole));
  10. Counter(1, 3, new Feedback(FeedbackToMsgBox)); // "Program." is optional
  11. Console.WriteLine();
  12. }
  13. private static void InstanceDelegateDemo() {
  14. Console.WriteLine("----- Instance Delegate Demo -----");
  15. //3、创建实例委托,用委托回调实例方法
  16. DelegateIntro di = new DelegateIntro();
  17. Counter(1, 3, new Feedback(di.FeedbackToFile));
  18. Console.WriteLine();
  19. }
  20. private static void Counter(Int32 from, Int32 to, Feedback fb) {
  21. for (Int32 val = from; val <= to; val++) {
  22. // If any callbacks are specified, call them
  23. if (fb != null)
  24. //4、调用这个委托
  25. fb(val);
  26. }
  27. }
  28. //2、声明一个方法包含要执行的代码
  29. private static void FeedbackToConsole(Int32 value) {
  30. Console.WriteLine("Item=" + value);
  31. }
  32. //2、声明一个方法包含要执行的代码
  33. private static void FeedbackToMsgBox(Int32 value) {
  34. MessageBox.Show("Item=" + value);
  35. }
  36. private void FeedbackToFile(Int32 value) {
  37. StreamWriter sw = new StreamWriter("Status", true);
  38. sw.WriteLine("Item=" + value);
  39. sw.Close();
  40. }
  41. }

委托对象是方法的包装器(wrapper),是方法能通过包装器来间接回调。如上的FeedbackToConsoleFeedbackToMsgBox方法通过委托包装,通过Counter方法来间接回调。
这个例子中的所有操作都是类型安全的。例如:在构造Feedback委托对象时,编译器确保FeedbackToConsoleFeedbackToMsgBox方法的签名兼容于Feedback委托类型定义的签名。具体的说,两个方法都要获取一个参数(一个int32),而且两者都熬有相同的返回类型(Void),将FeedbackToConsole的定义改为下面这样
private static Boolean FeedbackToCOnsole(string value){
···
}
C#编译器将不会编译以上代码,并报告一下错误:

error CS0123:"FeedbackToConsole"的重载均与委托"Feedback"不匹配

将方法绑定到委托时,C#和CLR都允许引用类型的协变性逆变性协变性是指方法能返回从委托的返回类型派生的一个类型。逆变性是指方法获取的参数可以是委托的参数类型的基类。
比如:

delegate object Mycallback(fileStream s);

完全可以构造该委托类型的一个实例并绑定到具有以下原型的方法

String SomeMethod(Stream s);

在这里,SomeMethod的返回类型String派生自委托的返回类型(Object),这是协变性;SomeMethod的参数类型Stream是委托的参数类型FileStream的基类,这是逆变性
注意只有引用类型才支持协变性和逆变性。

泛型委托

.NET Framework现在支持泛型,如返回void可用下面泛型

public delegate void Action();
public delegate void Action(T obj);
public delegate void Action(T1 arg1,T2 arg2);
public delegate void Action(T1 arg1,T2 arg2,T3 arg3);
...

事实上,.NET Framework现在提供了17个Action委托,它们从无参数到最多16个参数,使用起来非常方便。如果需要返回值,可使用Func函数。如果需要使用ref或out关键字以传引用的方式传递参数,就需要自己定义委托了。

简化语法

简化语法1:不要构造委托对象。

如:

ThreadPool.QueueUserWorkItem(SomeAsyncTask,5);

本来ThreadPool类的静态QueueUserWorkItem方法期待一个WaitCallback委托对象的引用,但你现在直接可以传递一个方法符合waitCallback类型就可以了。但C#编译器其实还是会生成waitcallback委托对象--只是语法简化了而已。

简化语法2:不需要定义回调方法(lambda表达式)

前面代码中,回调方法名称SomeAsyncTask传给ThreadPool的QueueUserWorkItem方法。如果方法较为简单可以直接写为:

ThreadPool.QueueUserWorkItem(obj=>Console.WriteLine(Obj),5);

编译器在看到则个lambda表达式后会生成一个匿名方法。新的语言规范建议开发人员多多使用lambda表达式语法。
书本中的用委托回调多个方法没有总结,个人觉得现在一个方法已经差不多了,等理解的好了再去研究调用多个方法。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注