.NET 委托与事件

来源:互联网 发布:c语言在线编译运行 编辑:程序博客网 时间:2024/06/05 03:34

“委托”基础

含义:
  • 委托(Delegate):是.NET Framework对C#和VB.NET等面向对象编程语言特性的一个重要扩充。
  • 是.NET中的一些重要技术,如事件、异步调用和多线程开发的技术基础。 
实例:FirstDelegateExample
  1. 定义一个MathOpt类,类中有一个 public int Add(int x, int y) 的方法。
  2. 定义一个委托类型,它有两个int类型的参数,返回一个int类型的值(public delegate int MathOptDelegate(int value1, int value2);),其参数与返回值与上面定义的方法一致。
  3. 在Porgram(class Program)类中的Main方法中,定义MathOptDelegate类型的委托变量(委托是一种用户自定义的数据类型,可以用于定义变量);
  4. 委托变量接收一个方法的引用,调用时像普通方法一样传入参数就可以使用。
结论:
  1. 委托可以看成是一个方法的“容器”,将某一具体的方法“装入”后,就可以把它当成方法一样调用。
  2. 一个委托类型的变量,可以引用任何一个满足其要求的方法(是方法,而不是数据某一类型的变量,类似于C语言的“函数指针”,但又不仅仅是“函数指针”,还可以实现异步调用)。


详解委托类型:






委托的组合与分解
  • 一个委托变量可以使用“+=”不断“挂接”多个方法,也能使用“-=”动态移除某个方法引用。
  • 引用多个方法的委托变量称为“多路委托”。
  • 调用该委托时,委托调用列表中的方法会依次执行。
实例:MulticastDelegateInvocation
  1. 声明一个委托(public delegate int MyDelegate(int value););
  2. 定义一个MyClass类,类中的两个方法 Func1 和 Func2 符合委托的要求;
  3. 在Program类中,定义 MyDelegate 委托变量del1,挂接方法(MyDelegate del1 = obj.Func1; del1 += obj.Func2;);
  4. 获取委托变量的方法调用列表(Delegate[] ds = del1.GetInvocationList(););
  5. 调用(del1(5);),将先调用 obj.Func1(),再调用 obj.Func2();
  6. 再定义一个 MyDelegate 委托变量 del2,并挂接方法;
  7. 组合委托:MyDelegate mul = del1 + del2;(其作用是首尾拼接成一个新的委托变量,方法列表中拥有4个方法)

从前面的例子看,使用委托只要有这几步:
  1. 定义委托类型;
  2. 定义一个或多个符合委托类型要求的方法;
  3. 定义委托类型的变量;
  4. 将第2步定义的方法引用使用“+=”“挂接”到第3步定义个变量,以形成一个“方法调用列表”;
  5. 通过委托变量“间接”调用方法调用列表。
  • 可见,比较麻烦,也可以使用“匿名方法”进行简化
匿名方法(其实是将方法定义与委托变量赋值连个步骤合并):
  1. 定义一个委托(public delegate int AddDelegate(int i, int j););
  2. 利用匿名方法的特性直接给委托变量赋值(AddDelegate del = delgate(int i, int j) { 处理并放回int值; } )。
匿名方法的使用:
  1. 委托类型作形参:
    1. 使用委托类型参数的方法:public static void invokeDelegate(AddDelegate del, int i ,int j) { 处理; }
    2. 直接将匿名方法作为函数参数:invokeDelegate(delegate(int i, int j) { 处理并返回int值; }, 100, 200);
    3. 或者使用Lambda表达式(匿名方法的简写)进一步简化:invokeDelegate((i, j) => i + j, 100, 200);

在编程中应用委托
  • 委托的本质特征是“一对多”(一个委托变量对应多个方法)。
实例:定时回调 UseTimeCallback(每各一秒显示当前时间)


.NET基类库中的委托应用

  • 泛型委托
示例:
  1. 定义泛型委托:public delegate T MyGenericDelegate<T>(T obj);;
  2. 使用匿名方法给泛型委托变量赋值:MyGenericDelegate<int> del = delegate(int value){ 处理并返回int值; };;
  3. 或使用Lambda表达式给放行委托变量赋值:MyGenericDelegate<int> del2 = (value) => value * 2;;
  4. 调用泛型委托变量引用匿名方法:del(100);。 
  • .NET基类库中预定义的委托
例如:
  • Func<> 委托:接收有返回值的方法
    • 应用实例:
      1. 使用匿名方法:Func<int, int, long> add = delegate(int a, int b){ 处理并返回long值; };;
      2. 使用Lambda表达式:Func<int ,int ,int> subtract = (a, b) => a - b;
      3. 调用:add(5, 10); subtract(10, 5);
    • 使用预定义的委托,可以省去反复定义委托类型的麻烦。
  • Action<> 委托:接收没有返回值的方法
  • 掌握委托是.NET程序员的基本要求!

委托与事件

事件
  • 在“事件驱动”的软件系统中,符合某种预设条件的情形出现时,一个事件被触发。
  • 事件三要素:
    • 事件源,即激发事件的对象;
    • 事件信息,即事件本身所携带的信息;
    • 事件响应者,即响应事件的代码,这些代码决定了当事件发生时,需哟啊计算机完成的工作。
实例:鼠标坐标实时显示



  • “事件驱动”的软件运行方式:
    • 当某个事件触发时,程序员事先写好的响应此事件的代码被调用。
  • “事件驱动”的软件开发方式:
    1. 定义并实现自己的事件,或从系统组件库中选择一种现成的事件;
    2. 为这一事件编写响应代码。
自定义事件
  • 事件的主要特点为一对多关联,即一个事件源,可以有多个响应者;
  • 委托与它所引用的方法也具有一对多的关联,因此启发:将事件看成是一个多路委托变量,而事件的响应方法则被此多路委托变量所引用。
  1. 使用委托实现自定义事件:
    1. 定义事件委托:public delegate void MyEventDelegate(int value);(事件携带着int value信息)
    2. 定义事件发布者类:public class Publisher{ 使用多路委托变量保存多个事件响应者的方法引用:public MyEventDelegate MyEvent; }
    3. 定义事件响应者:public class Subdcriber{ 事件触发时的回调方法:public void MyMethod(int value){xxx} }
  2. 使用关键字event定义自定义事件:
    1. 定义事件发布者类:public class Publisher{}
      1. 使用关键字event定义一个事件:public event MyEventDelegate MyEvent;
      2. 激发事件:public void FireEvent(int EnentArgu){ if (MyEvent != null) MyEvent(EventArgu); }
  • event关键字定义的事件只能由事件源对象自己激发,外界无法通过访问委托变量直接激发事件。

.NET事件机制剖析

  • 场景:当在windows Forms 应用程序中给某个控件事件编写了事件响应方法时,但又在代码编辑器中删除了这个方法,项目编译将失败。
  • 通过测试跟踪得出:
  • Click事件的定义如:public event EnventHandler Click;
  • Click事件的响应方法必须符合EventHandler委托的要求:public delegate void EventHandler(object sender(事件源), EventArgs e(事件信息))
  • 结论:.NET事件触发与响应机制是建立在委托之上的。

实例:
  • 运行时设定事件的响应函数
    • public partial class frmMain: Form
    • {
      • public frmMain()
      • {
        • InitializeComponent();
        • //运行时才设定的事件响应函数
        • button1.Click += new EnentHandler(button1_Click);
      • }
      • void button1_Click(object sender, EnentArgs e)
      • {
        • MessageBox.Show("I'm Clicked!");
      • }
    • }
  • 实例二:动态挂接代码
  • 实现:
  1. 在frmMain类中,定义字段:
    1. private int LoanMoney = 0; //借的钱
    2. private int LoanCount = 0; //借钱次数
  2. 在frmMain类中, 定义借钱的方法:private void LoanFormHuang()
    1. btnSum.Enable = true;
    2. LoanMoney = 0;
    3. btnSum.Click += new EnentHandler(btnSum_Click); //追加事件响应函数
    4. lblLoanCount.Text = string.Format("{0}次", ++LoanCount);
  3. 在frmMain类中,定义查看“杨白劳欠了黄世仁多少钱”的方法:private void ShowLoanState()
    1. LoanMoney += 100; //每次点击按钮增加100元
    2. lblLoanMoney.Text = string.Format("{0}元", LoanMoney);
    3. lblLoanMoney.Refresh();
    4. Thread.Sleep(300); //使其有动画效果
    5. btnSum.Enabled = flase;
  4. 在frmMain类中,定义:private void btnLoanFromHuang_Click(object sender, EventArgs e)
    1. LoanFromHuang();
  5. 在frmMain类中,定义:void btnSum_Click(object sender, EventArgs e)
  • 实例三:使用一个方法响应多个对象的同种类型的事件:按回车焦点直接切换下一个输入框中
  • 实现:
  1. 将“回车移动焦点”封装成一个事件响应函数:private void EnterToTab(object sender, KeyEventArgs e)
    1. groupBox1.SelectNextControl(sender as Control, true, true, true, true);
    2. 设置拥有焦点的文本框自动全选:
      1. txt = (sender as TextBox);
      2. if(txt != null) txt.SelectAll();
  2. 在窗体的构造函数(public frmEnterToTab())中:
    1. InitializeComponent();
    2. 挂接事件,把事件响应函数挂接到所有文本框上:
      1. foreach(Control ctl in groupBox1.Controls)
      2. {
        1. if (ctl is TextBox)
          1. (ctl as TextBox).keyDown += this.EnterToTab;
      3. }

.NET自定义事件实用开发指导

技术场景:
  • 设计一个可统计点击次数的按钮,它提供一个自定义的MyClick事件
  • 实现方法一:
  1. 定义一个事件参数类 MyClickEventArgs,从EventArgs类派生,它所携带的事件信息是:按钮的单击次数 ClickCount
  2. 为参数类定义一个事件委托:
  3. 继承Button类实现在普通按钮的基础上添加“统计点击次数”的功能:
  4. 重写OnClick方法:
  5. 将该控件编译好后,其将作为新控件出现在工具栏上。
  • 实现方法二:使用泛型事件委托类型(为了简化自定义事件开发,.NET基类库中定义了一个通用的泛型事件委托)

0 0
原创粉丝点击