1.1.1 定义

  委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值,如下面的示例所示:

  //Code in C#

  public delegate int PerformCalculation(int x, int y);

  与委托的签名(由返回类型和参数组成)匹配的任何方法都可以分配给该委托。

  简单理解Delegate委托(或代理)是一种数据类型:它的变量可以引用到某一个符合要求的方法上,通过委托可以间接地调用该方法。

  其实.NET的委托类似于C语言的函数指针,区别在于.NET委托是类型安全的,这说明,C中的函数指针只不过是一个指向存储单元的指针,我们无法说出这个指针实际指向什么。

1.1.2 委托使用

  • 使用委托的四部曲:
  • 定义一种委托类型
  • 委托执行时要调用方法
  • 定义一个委托实例
  • 委托实例的调用

  我们先定义一种委托类型如下:

  1.  
  2. //自定义一种委托类型
  3.  
  4. public delegate void StringProcessor(string input);
  5.  
  6. 然后我们再定义5中候选的委托方法如下:
  7.  
  8. void PrintString(string x)
  9.  
  10. void PrintInteger(int x)
  11.  
  12. void PrintTwoStrings(string x, string y)
  13.  
  14. int GetStringLength(string x)
  15.  
  16. void PrintObject(object x)

  大家猜猜看哪个和上面提供的委托类型签名匹配(签名匹配:参数类型,参数个数和返回类型匹配)。激动时刻到了马上公布答案,和委托类型匹配的方法是PrintString和PrintObject,如果有不明白的请细细考虑一下委托匹配的条件—签名匹配。

图1委托成功输出

  现在对委托有了一定的认识,接下来我们将介绍委托最经常使用的地方—事件。

  我们将从发送器和接受器的角度讨论事件,例如在UI编程中,鼠标单击或键盘按键,发送器就是.NET的CLR,注意事件发送器并不知道接收器是谁,这符合面向对象的原则,而且某个事件接收器有个方法处理该事件,这个时候就要委托,如前面所讲事件发送器对事件接收器一无所知,通过委托作为一个中介,接收器把事件处理方法注册到事件中,这样就实现了由发送器->委托->接收器的过程了。

  我们可以这样认为:委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

 

1.1.3 自定义委托

  前面话有点难以理解,接下来我们通过具体的例子分析一下何谓委托,该如何实现委托。现在不是很喜欢搞多国语言化的吗?看看如何让我们的程序会说多种语言吧!

  1.  
  2. /// <summary>
  3. /// the English speaker.
  4. /// </summary>
  5. /// <param name="name">The name.</param>
  6. public void EnglishSpeaker(string name)
  7. {
  8. Console.WriteLine(
  9. string.Format("Hello my name is {0} and I am English speaker.\n", name));
  10. }
  11.  
  12. /// <summary>
  13. /// the Chineses speaker.
  14. /// </summary>
  15. public void ChineseSpeaker(string name)
  16. {
  17. Console.WriteLine(
  18. string.Format("您好我的名字叫{0},我是讲普通话的。\n", name));
  19. }

  好啦现在我们有两个方法分别是说普通话和英语,现在我们的程序会说普通话和英语啦。现在我们考虑究竟什么时候讲普通话什么时候讲英语,那不简单我们加个判断就OK啦,是的我们可以通过switch或者if else就可以实现啦。

  1.  
  2. /// <summary>
  3. /// 根据上下文调用不同的方法
  4. /// </summary>
  5. /// <param name="name">string</param>
  6. /// <param name="lang">enum</param>
  7. private static void Say(string name, Language lang)
  8. {
  9. switch (lang)
  10. {
  11. case Language.Chinese:
  12. Program.ChineseSpeaker(name);
  13. break;
  14. case Language.English:
  15. Program.EnglishSpeaker(name);
  16. break;
  17. default :
  18. break;
  19. }
  20. }

  但假设我们现在又要增加新的语言西班牙语,同样我们可以增加西班牙语,但我们必须修改switch语句增加判断,这不符合OOP中的OCP(对扩展开放,对修改关闭原则),这时候委托该登场。

  1.  
  2. /// <summary>
  3. /// Define speak delegate.
  4. /// </summary>
  5. /// <param name="name"></param>
  6. private delegate void SpeakDelegate(string name);

  

  首先我们定义了一种委托类型SpeakDelegate,然后我们通过修改Say方法看看该如何使用委托变量。

  1.  
  2. /// <summary>
  3. /// The base say function.
  4. /// </summary>
  5. /// <param name="name">The name.</param>
  6. /// <param name="speaker">The speaker.</param>
  7. private static void Say(string name, SpeakDelegate speaker)
  8. {
  9. ///Inoke the speaker function.
  10. speaker(name);
  11. }

  现在我们的参数已经不是枚举类型了,而是一个委托类型变量,而且实现的具体代码也没有了switch语句了,比之前简单了许多。现在大家知道如何去调用Say方法吧!没错我们只需传递一个name和一个具体实现函数名就OK了。

  1.  
  2. ///传递函数名进行委托方法绑定
  3. Program.Say("钧航", ChineseSpeaker);
  4. Program.Say("JK.Rush", EnglishSpeaker);

  自定义委托相信大家都会了,接下来我将介绍一下.NET中委托实现,由于许多使用委托的例子都是事件,所以下面的例子也采用事件。但请大家要注意“可以使用委托,但却没有定义事件”的情况(例如:回调函数)。

1.1.4 .NET中的事件委托

  举一个简单的例子,.NET中经常使用的控件Button,当我们把Button 控件 drap and drop到界面,然后双击界面的Button我们发现程序中自动生成了一个响应Button的事件方法,然后我们给事件方法添加Code之后,当我们点击该Button就响应该方法了,但我们没有看到代码中有任何的委托和事件之类的定义,其实这些.NET都已经做好了。我们可以查看如下文件。

              图2事件委托实现

  如上图所示我们打开Designer文件,事件委托的实现都在这里实现了。

其中,EventHandler就是一个代理类型,可以认为它是一个“类”,是所有返回类型为void,具备两个参数分别是object sender和EventArgs e,第一个参数表示引发事件的控件,或者说它表示点击的那个按钮。通过以下的代码我们细细解析一下。

  1.  
  2. private void button1_Click(object sender, EventArgs e)
  3. {
  4. //获取被点击Button的实例
  5. Button objBotton = sender as Button;
  6. if (objBotton != null)
  7. {
  8. objBotton.Text = "Hello you click me.";
  9. objBotton.AutoSize = true;
  10. }
  11. else
  12. {
  13. //Exception Handle.
  14. }
  15. }

图3点击产生效果

  

  OK现在明白了sender就是传递一个被点击对象的实例,第二个参数名叫e的EventArgs参数,用于      表示附加的事件关联的事件信息。当点击按钮时,没有附加任何关联的事件信息,如上的点击事件,第二参数并不表示任何有用的信息。但什么时候会用到呢?

  我们先介绍一下EventArgs这个的类型。其实这个类并没有太多的功能,它主要是作为一个基类让其他类去实现具体的功能和定义,当我们搜索EventArgs发现很多类是继承于它的。

  1.  
  2. public class EventArgs
  3. {
  4. // Fields
  5. public static readonly EventArgs Empty;
  6.  
  7. // Methods
  8. static EventArgs();
  9. public EventArgs();
  10. }

  举其中的ImageClickEventArgs为例,它继承于EventArgs,而且还添加了自己的字段用来基类X和Y的坐标值(这是一个ImageButton被点击时候响应的),然后获取该按钮的X和Y坐标。

  1.  
  2. public sealed class ImageClickEventArgs : EventArgs
  3. {
  4. // Fields
  5. public int X;
  6. public int Y;
  7.  
  8. // Methods
  9. public ImageClickEventArgs(int x, int y)
  10. {
  11. this.X = x;
  12. this.Y = y;
  13. }
  14. }
  1.  
  2. //ImageButton点击响应时间
  3. protected void ibtnTest_Click(object sender, ImageClickEventArgs e)
  4. {
  5. this.lblCX.Text = e.X.ToString();
  6. this.lblCY.Text = e.Y.ToString();
  7. }

图4获取ImageClickEventArgs关联点击坐标

  前面提到其他事件关联信息类型都是通过继承EventArgs实现的,所以说我们自己也可以自定义一个事件关联信息类型,如下我们只需继承EventArgs就OK了。

  1.  
  2. /// <summary>
  3. /// 自定义事件关联类
  4. /// </summary>
  5. public class ColorChangedEventArgs : EventArgs
  6. {
  7. private Color color;
  8.  
  9. /// <summary>
  10. /// Initializes a new instance of the <see cref="ColorChangedEventArgs"/> class.
  11. /// </summary>
  12. /// <param name="c">The c.</param>
  13. public ColorChangedEventArgs(Color c)
  14. {
  15. color = c;
  16. }
  17.  
  18. /// <summary>
  19. /// Gets the color of the get.
  20. /// </summary>
  21. /// <value>
  22. /// The color of the get.
  23. /// </value>
  24. public Color GetColor
  25. {
  26. get { return color; }
  27. }
  28.  
  29. }

1.1.5自定义事件委托

多播委托

  前面使用的每个委托都只包含一个方法调用。调用一个委托就调用一个方法调用。如果要通过一个委托调用多个方法,那就需要使用委托的多播特性。如果调用多播委托,就可以按委托添加次序连续调用多个方法。为此,委托的签名就必须返回void;否则,就只能得到委托调用的最后一个方法的结果,接下来看看多播实现。

  1.  
  2. namespace Multi_Delegate
  3. {
  4. delegate void StringProcessor();
  5. public class Person
  6. {
  7. private string _Name;
  8. public Person(string name)
  9. {
  10. this._Name = name;
  11. }
  12.  
  13. public void Say()
  14. {
  15. Console.WriteLine("Hello my name is {0}, what's your name.\n", this._Name);
  16. }
  17.  
  18. public void Reply()
  19. {
  20. Console.WriteLine("Hello my name is {0} and nice to meet you.\n", this._Name);
  21. }
  22. }
  23.  
  24. class Program
  25. {
  26. static void Main(string[] args)
  27. {
  28. Person Jack = new Person("Jack");
  29. Person Oliver = new Person("Oliver");
  30. StringProcessor sp = null;
  31. //绑定多播方法调用
  32. sp += Jack.Say;
  33. sp += Oliver.Reply;
  34. sp();
  35. Console.ReadKey();
  36. }
  37. }
  38. }

  也许有人觉得很简单,实现的确简单明了,就是通过“+”把方法调用绑定到委托变量中,如果我们用“-”就可以移除绑定到委托变量方法了。

 事件

  前面一直没有解释什么是事件,现在让我用一句话解释事件和委托的关系吧!

  事件和委托关系就像是属性和字段的关系,为了刚好的实现OOP的编程原则,事件对委托进行了封装。

  现在我们修改前面的代码,使用事件对委托进行封装。

  1.  
  2. /// 使用事件对委托进行封装
  3. /// </summary>
  4. public class Say
  5. {
  6. /// <summary>
  7. /// 封装委托字段
  8. /// </summary>
  9. public static event SpeakDelegate speakDelegate;
  10.  
  11. /// <summary>
  12. /// 调用委托具体实现方法
  13. /// </summary>
  14. /// <param name="name"></param>
  15. public static void SayManager(string name)
  16. {
  17. speakDelegate(name);
  18. }
  19. }
  20.  
  21. /// <summary>
  22. /// 客户端调用委托
  23. /// </summary>
  24. /// <param name="args"></param>
  25. static void Main(string[] args)
  26. {
  27. Say.speakDelegate += Program.ChineseSpeaker;
  28. Say.speakDelegate += Program.EnglishSpeaker;
  29. Say.SayManager("Jackson");
  30. Console.ReadKey();
  31. }

图5自定义委托

  现在让我们看看编译后Say类就可以充分证明我们的结论:事件是对委托封装。

图6自定义事件编译后的代码

  大家看到在编译后的代码中出现了一个私有的委托变量,然后接下是一个公用事件委托变量,这进一步说明了事件是对委托的封装。

图7自定义事件编译后MSIL代码

1.1.6事件委托实现观察者模式

  前面我们介绍按钮事件响应是从发送者和接收者的角度出发的,现在我们以设计模式中的观察者模式为例。

图8GoF观察者架构

  1.  
  2. namespace GoFObserver
  3. {
  4. /// <summary>
  5. /// 充当Subject角色
  6. /// </summary>
  7. public class GofTelecom
  8. {
  9. public delegate void GofNews();
  10. public static event GofNews NewEvent;
  11.  
  12. /// <summary>
  13. /// 发布通知方法
  14. /// </summary>
  15. /// <returns></returns>
  16. public static bool Notify()
  17. {
  18. if (NewEvent != null)
  19. {
  20. NewEvent();
  21. return false;
  22. }
  23. return true;
  24. }
  25. }
  26.  
  27. public interface IObserver
  28. {
  29. void Update();
  30. }
  31.  
  32. /// <summary>
  33. /// 观察者
  34. /// </summary>
  35. public class Programmer : IObserver
  36. {
  37.  
  38. #region IObserver 成员
  39.  
  40. public void Update()
  41. {
  42. Console.WriteLine("I am a greenhand programmer.\n");
  43. }
  44.  
  45. #endregion
  46.  
  47. }
  48.  
  49. /// <summary>
  50. /// 观察者
  51. /// </summary>
  52. public class Architect : IObserver
  53. {
  54. #region IObserver 成员
  55.  
  56. public void Update()
  57. {
  58. Console.WriteLine("OH...I am a top banana.\n");
  59. }
  60.  
  61. #endregion
  62. }
  63.  
  64. public class Program
  65. {
  66. static void Main(string[] args)
  67. {
  68. IList<IObserver> objObserver = new List<IObserver>();
  69. objObserver.Add(new Programmer());
  70. objObserver.Add(new Architect());
  71. foreach (IObserver ob in objObserver)
  72. {
  73. GofTelecom.NewEvent += ob.Update;
  74. }
  75.  
  76. if (!GofTelecom.Notify())
  77. {
  78. Console.WriteLine("Notify successful.\n");
  79. }
  80. else
  81. {
  82. Console.WriteLine("Notify failed.\n");
  83. }
  84. Console.ReadKey();
  85. }
  86. }
  87. }

.NET 中的委托的更多相关文章

  1. C# 中的委托和事件

    觉得这篇文章写的非常好,大神之作,由简入繁,对我这种初学者来说帮忙很大,特此留存下. 摘自:http://tracefact.net/CSharp-Programming/Delegates-and- ...

  2. Objective-C中的委托(代理)模式

    我个人更喜欢把委托(Delegate)模式称为代理(Proxy)模式.还是那句话,第一次接触代理模式是在Java中接触的,在Java中实现代理模式和接口是少不了的.当时学习Spring的时候用到了接口 ...

  3. C# 中的委托和事件(转)

    引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去 ...

  4. C# 中的委托和事件(转载)

    引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去 ...

  5. 【转】C# 中的委托和事件

    阅读目录 C# 中的委托和事件 引言 将方法作为方法的参数 将方法绑定到委托 事件的由来 事件和委托的编译代码 委托.事件与Observer设计模式 .Net Framework中的委托与事件 总结 ...

  6. 第3章 C#中的委托和事件

    .NET框架中的委托和事件 using System; using System.Collections.Generic; using System.Linq; using System.Text; ...

  7. 分分钟用上C#中的委托和事件之窗体篇

    上次以鸿门宴的例子写了一篇名为<分分钟用上C#中的委托和事件>的博文,旨在帮助C#初学者迈过委托和事件这道坎,能够用最快的速度掌握如何使用它们.如果觉得意犹未尽,或者仍然不知如何在实际应用 ...

  8. 《C#高级编程》学习笔记------C#中的委托和事件(续)

    本文转载自张子阳 目录 为什么要使用事件而不是委托变量? 为什么委托定义的返回值通常都为void? 如何让事件只允许一个客户订阅?(事件访问器) 获得多个返回值与异常处理 委托中订阅者方法超时的处理 ...

  9. c#中的委托和事件(转)

    引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去 ...

  10. C#中的委托和事件(续)

    转自张子阳的博客http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-Advanced.aspx 引言 如果你看过了 C#中 ...

随机推荐

  1. new在c#方法中的使用

    new在c#中有三种用法: 1.实例化对象 2.泛型约束 3.用在方法前.new和override的区别在于:override用于重写父类的方法:new用于隐藏方法,它调用的方法来自于申明的类,如果申 ...

  2. Linux下java获取CPU、内存、磁盘IO、网络带宽使用率

    一.CPU 使用proc文件系统,"proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间.它以文件系统的方式为访问系统内核数据的操作提供接口.用户和应用程序可以通过proc得 ...

  3. 验证标题是否存在(TextBox控件失去焦点验证)

    首先解释两个属性, AutoPostBack 属性用于设置或返回当用户在 TextBox 控件中按 Enter 或 Tab 键时,是否发生自动回传到服务器的操作. 如果把该属性设置为 TRUE,则启用 ...

  4. imread() not working in OpenCV 2.4.11 Debug mode

    The OpenCV function imread() not working in OpenCV 2.4.11 Debug mode of VS2010 under Win32, the way ...

  5. [FollowUp] Combinations 组合项

    这是Combinations 组合项 的延伸,在这里,我们允许不同的顺序出现,那么新的题目要求如下: Given two integers n and k, return all possible c ...

  6. ACM对时间掌控力和日积月累的习惯的意义

    马云说,要想创业成功,不是要知道现在什么东西最火,而是要清楚的知道十年以后什么东西最火.这就意味着,你对时间掌控力,至少要有十年. 但是仔细回想一下自己的学生时代,自己对时间的把握是怎样的?有些人只能 ...

  7. 安装Bind过程中提示丢失MSVCR110.dll的解决办法

    前几天在线安装Visual Studio 2012 Update 3,由于在线安装需要不断下载安装文件,时间很长,后来等不下去,就取消了,不幸的是VS启动不了了,弹出“devenv.exe – 系统错 ...

  8. POJ 1088 滑雪(记忆化搜索)

    滑雪 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 92384   Accepted: 34948 Description ...

  9. UITableview xib里面 cell 按钮的回调

    //  MoreBtnCell.m#import <UIKit/UIKit.h> @interface MoreBtnCell : UITableViewCell @property (w ...

  10. Win10开始菜单打不开?两个办法可破

    很多人在安装Windows10后,都遇到过开始菜 单无法打开和Cortana框架无法输入文字的问题, 这种问题在系统更新后特别频繁.Windows会报错 为关键错误,并提示在下次登录会进行解决,同时要 ...