ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

C# 事件(标准事件的用法)

C# 事件(标准事件的用法)

标准事件的用法

GUI编程是事件驱动的,也就是说在程序运行时,它可以在任何时候被事件打断,比如按钮
点击、按下按键或系统定时器。在这些情况发生时,程序需要处理事件然后继续做其他事情。

显然,程序事件的异步处理是使用C#事件的绝佳场景。WindowsGUI编程如此广泛地使用
了事件,以至于对于事件的使用,.NET框架提供了一个标准模式。该标准模式的基础就是System
命名空间中声明的EventHandler委托类型。EventHandler委托类型的声明如以下代码所示。关于
该声明需要注意以下几点。

  • 第一个参数用来保存触发事件的对象的引用。由于它是object类型的,所以可以匹配任
    何类型的实例。
  • 第二个参数用来保存状态信息,指明什么类型适用于该应用程序。
  • 返回类型是void。
public delegate void EventHandler(object sender,EventArgs e);

EventHandler委托类型的第二个参数是EventArgs类的对象,它声明在System命名空间中。
你可能会想,既然第二个参数用于传数据,EventArgs类的对象应该可以保存某种类型的数据。
你错了。

  • EventArgs不能传递任何数据。它用于不需要传递数据的事件处理程序一一通常会被忽略。
  • 如果你希望传递数据,必须声明一个派生自EventArgs的类,并使用合适的字段来保存需
    要传递的数据。

尽管EventArgs类实际上并不传递数据,但它是使用EventHandler委托的模式的重要部分。
不管参数的实际类型是什么,object和EventArgs类型的参数总是基类。这样EventHandler就能
提供一个对所有事件和事件处理器都通用的签名,让所有事件都正好有两个参数,而不是各自都
有不同的签名。
如图15-6所示,我们修改lncrementer程序,使之使用EventHandler委托。注意以下几点。

  • 在声明中使用系统定义的EventHandler委托替换Handler。
  • 订阅者类中的事件处理程序声明的签名必须与事件委托(现在使用object和EventArgs
    类型的参数)的签名(和返回类型)匹配。对于IncrementDozensCount事件处理程序来
    说,该方法忽略了形参。
  • 触发事件的代码在调用事件时必须使用适当的参数类型的对象。
class Incrementer //发布者
{public event EventHandler CountedADozen;     //使用系统定义的EventHandler委托public void DoCount(){for(int i=1;i<100;i++)if(i%12==0&&CountedADozen!=null)CountedADozen(this,null);        //使用事件时使用EventHandler的参数}
}class Dozens //订阅者
{public int DozensCount{get;private set;}public DOzens(Incrementer incrementer){DozensCount=0;incrementer.CountedADozen+=IncrementDozensCount;}void IncrementDozensCount(Object source,EventArgs e)   //事件处理程序的签名必须与委托的签名匹配{DozensCount++;}
}class Program
{static void Main(){Incrementer incrementer=new Incrementer();Dozens dozensCounter=new Dozens(incrementer);incrementer.DoCount();Console.WriteLine("Number of dozens={0}",dozensCounter.DozensCount);}
}

通过扩展EventArgs来传递数据

为了向自己的事件处理程序的第二个参数传入数据,同时遵循标准惯例,我们需要声明一个
派生自EventArgs的自定义类,它可以保存我们需要传人的数据。类的名称应该以EventArgs结
尾。例如,如下代码声明了一个自定义类,它能将字符串存储在名为Message的字段中。

//自定义类, 基类
public class IncrementerEventArgs:IncrementerEventArgs
{public int IterationCount{get;set;}  //存储一个整数
}

现在我们有了一个自定义的类,可以向事件处理程序的第二个参数传递数据,所以你需要一
个使用新自定义类的委托类型。为此,可以使用泛型版本的委托EventHander<>。第18章将详
细介绍C#泛型,现在你只需要观察。要使用泛型委托,需要做到以下两点,如随后的代码所示。

  • 将自定义类的名称放在尖括号内。
  • 在需要使用自定义委托类型的地方使用整个字符串。例如,event声明可能为如下形式:

image
下面我们在处理事件的其他4部分代码中使用自定义类和自定义委托。例如,下面的代码更
新了lncrementer,使用自定义的EventArgs类IncrementerEventArgs和泛型EventHand1er委托。

public class IncrementerEventArgs:EventArgs   //自定义类派生自EventArgs
{public int IterationCount{get;set;}       //存储一个整数
}class Incrementer   //使用自定义类的泛型委托
{public event EventHandler<IncrementerEventArgs>CountedADozen;//使用自定义类的泛型委托public void DoCount()    //自定义类对象{IncrementerEventArgs args=new IncrementerEventArgs();for(int i=1;i<100;i++)if(i%12==0&&CountedADozen!=null){args.IterationCount=i;CountedADozen(this,args); //在触发事件时传递参数}}
}class Dozens
{pbulic int DozensCount{get;private set;}public Dozens(Incrementer incrementer){DozensCount=0;incrementer.CountedADozen+=IncrementerEventArgs;}void IncrementDozensCount(object source,IncrementerEventArgs e){Console.WriteLine($"Incrmented at iteration:{e.IterationCount} in {source.ToString()}");DozensCount++;}
}class Program
{static void Main(){Incrementer incrementer=new Incrementer();Dozens dozensCounter=new Dozens(incrementer);incrementer.DoCount();Console.WriteLine($"Number of dozens={dozensCounter.DozensCount}");}
}

移除事件处理程序

在用完事件处理程序之后,可以从事件中把它移除。可以利用一运算符把事件处理程序从事
件中移除,如下所示:

p.SimpleEvent-=s.MethodB;   //移除事件处理程序MethodB

例如,下面的代码向事件添加了两个处理程序,然后触发事件。每个处理程序
都将被调用并打印文本行。然后将MethodB处理程序从事件中移除。当事件再次触发时,只有
MethodA处理程序会打印一行。

class Publisher
{public event EventHandler SimpleEvent;public void RaiseTheEvent(){SimpleEvent(this,null);}
}class Subscriber
{public void MethodA(object o,EventArgs e){Consoel.WriteLine("AAA");}public void MethodB(object o,EventArgs e){Console.WriteLine("BBB");}
}class Program
{static void Main(){Publisher p=new Publisher();Subscriber s=new Subscriber();p.SimpleEvent+=s.MethodA;p.SimpleEvent+=s.MethodB;p.RaiseTheEvent();Console.WriteLine("\r\nRemove MethodB");p.SimpleEvent-=s.MethodB;p.RaiseTheEvent();}
}

如果一个处理程序向事件注册了多次,那么当执行命令移除处理程序时,将只移除列表中该
处理程序的最后一个实例。

返回列表