logo资料库

C#和Vb 的多播委托 事件.doc

第1页 / 共17页
第2页 / 共17页
第3页 / 共17页
第4页 / 共17页
第5页 / 共17页
第6页 / 共17页
第7页 / 共17页
第8页 / 共17页
资料共17页,剩余部分请下载后查看
委托
通过使用 Delegate 类间接调用应用程序方法
通过使用新的 C# 2.0技术增强委托行为
(1)匿名方法
(2)协变
(3)逆变
事件
使用事件的好处
事件的工作方式
通过使用 Event 语句创建事件
(4)使用VB.NET实现
(5)使用C#实现
EventHandler 委托的实现方法。
(6)VB.NET
(7)C#
自定义事件参数类
事件和委托的关系
.NET Framework 使 VB.NET 真正具有了面向对象的功能,并且还引入了一种完全面向 对象的语言 C#。通过使用这些语言,用户可以创建模拟实际商业环境的健壮的事件驱动的 应用程序。例如,在现实世界中,操作人员通过对事件做出响应来交互。当某个操作人员执 行操作并触发事件后,另一个操作人员会根据这个事件执行相应的操作,这就形成了一个对 话或场景。这些操作相互关联,但由相互独立的操作人员执行。为了反映现实世界,面向对 象的程序需要这样的机制:在保持对象之间交互能力的同时允许对象实例之间相互独立。事 件和委托就提供了这样的一种机制。 委托 图 4- 1 委托 委托是引用类型,这与类和接口相似。用户可以声明委托类型,定义委托类型的变量或 者创建委托的实例。委托可以在运行时间接调用一个或多个方法。在声明委托类型或基于委 托类型的变量时并不指定委托将要调用哪些方法,这一操作是在创建委托的实例时才进行 的,并且在运行时还可以将一个或多个方法与委托动态关联,如图 4- 1 所示。委托与 C++ 中的函数指针类似。但是,与 C++ 函数指针不同的是,它们是类型安全的对象,并且具有 各自属性和方法。另外,委托的签名(包含参数和返回值的类型)必须与其调用的方法的签 名匹配。下面的代码演示了如何实现委托。 [C#] //声明一个委托 public delegate void Handler(Object obj); //声明和委托签名相同的方法 public void DoSomething1(Object obj) { //在方法中执行操作。 1
} public void DoSomething2(Object obj) { //在方法中执行操作。 } //在方法中使用委托 public void UseHandler() { //创建一个委托,这时只有一个方法与之关联,它就是单路广播委托。 Handler handler = new Handler(DoSomething1); //关联第二个方法,它就是多路广播委托。 handler += new Handler(DoSomething2); //调用委托,效果和调用与之关联的方法相同。 handler("测试对象"); } [VB.NET] '声明一个委托 Public Delegate Sub Handler(ByVal obj As Object) '声明和委托签名相同的方法 Public Sub DoSomething1(ByVal obj As Object) '在方法中执行操作。 End Sub Public Sub DoSomething2(ByVal obj As Object) '在方法中执行操作。 End Sub '在方法中使用委托 Public Sub UseHandler() '创建一个委托,这时只有一个方法与之关联,它就是单路广播委托。 Dim handler1 As New Handler(AddressOf DoSomething1) Dim handler2 As New Handler(AddressOf DoSomething2) '关联第二个方法,它就是多路广播委托。 Dim handler3 As Handler = Handler.Combine(handler1, handler2) handler3.Invoke("测试数据") End Sub 在 C#中,用户可以创建匿名方法。要创建匿名方法,在实例化委托时需要创建方法签 名并提供内联代码块。只要签名匹配,委托就可保存对多个已命名方法的引用。如果委托只 引用一个方法,则认为该委托是单路广播委托。当向委托添加更多指向其他方法的引用时, 这些引用将被存储在委托的调用列表中,这种委托就是多路广播委托。所有委托都是隐式的 多路广播委托。通过向一个委托的调用列表添加多个方法引用,可以通过调用该委托一次调 用所有的方法。这一过程称为多路广播。用户也可以单独调用调用列表中的每个方法。如果 2
想要确定每个方法调用的返回值或输出参数或者如果不想在特定环境中调用特定的方法,这 样做是有用的。 通过使用 Delegate 类间接调用应用程序方法 用户可以使用 Delegate 类实例化对象,使对象能作为指向函数、静态方法或实例方法 的引用。由于委托实例为引用类型,因此可将它们作为参数传递给其他方法。声明委托时, 除了指定返回值外还必须指定一个方法签名。当实例化委托时,需要引用该方法。该方法必 须与委托具有相同的方法签名,并且该方法必须返回委托声明中定义的值。调用委托时,委 托调用与之关联的方法。当引用的方法向委托返回值时,委托会将该值传递给调用程序,就 如同该值是委托自身的返回值一样。 下面说明了使用委托的步骤。在这些步骤中我们将创建 ReportPrinter 类,该类用于打 印来自应用程序的多个报告。该应用程序需要来自 ReportPrinter 类的反馈。这样,应用程序 就可以创建一个能从 ReportPrinter 类回调的方法。  步骤 1:声明委托 在该步骤中,首先创建一个名为 PrintStatus 的枚举。该枚举包含有关当前打印状态的 信息。接下来,声明一个名为 PrintInfoCallBack 的委托。调用应用程序具有一个与该方法 签名匹配的方法。 [C#] public enum PrintStatus { GeneratingReport, GeneratedReport, PrintingReport, PrintingComplete, Error } public delegate void PrintInfoCallBack(PrintStatus Status, object State); [VB.NET] Public Enum PrintStatus GeneratingReport GeneratedReport PrintingReport PrintingComplete [Error] End Enum Public Delegate Sub PrintInfoCallBack(ByVal Status As PrintStatus, _ ByVal State As Object)  步骤 2:创建 ReportPrinter 类,使用委托 在该步骤中我们创建一个在打印报告时调用的 PrintNextReport 方法的 ReportPrinter 类。 应用程序创建 ReportPrinter 类的一个实例并调用 PrintNextReport 方法。该方法接受基于在步 骤 1 中创建的 PrintInfoCallBack 委托数据类型的参数。四次调用名为 CallBack 的 PrintInfoCallBack 委托:在报告生成前、在报告生成后、在开始打印报告前以及在完成报告 打印后。每次都会向 CallBack 方法返回不同的状态。 3
[C#] public class ReportPrinter { public void PrintNextReport(PrintInfoCallBack CallBack, object State) { CallBack(PrintStatus.GeneratingReport, State); CallBack(PrintStatus.GeneratedReport, State); CallBack(PrintStatus.PrintingReport, State); CallBack(PrintStatus.PrintingComplete, State); } } [VB.NET] Public Class ReportPrinter Public Sub PrintNextReport(ByVal CallBack As PrintInfoCallBack, _ ByVal State As Object) CallBack(PrintStatus.GeneratingReport, State) CallBack(PrintStatus.GeneratedReport, State) CallBack(PrintStatus.PrintingReport, State) CallBack(PrintStatus.PrintingComplete, State) End Sub End Class 使用回调时应当小心。在这种情况下,ReportPrinter 类不知道在调用委托时将发生的事 件。发生的事件由调用应用程序决定。应该避免在性能敏感或安全敏感方法中使用回调模式, 这是因为调用应用程序可能无法正常执行回调或回调的执行方式可能不安全。  步骤 3:创建委托调用的方法 在该步骤中,我们使用 ReportPrinter 类创建一个控制台应用程序并添加一个签名与 PrintInfoCallBack 委托匹配的方法。从 ReportPrinter 类回调 GetPrintInfo 方法并向命令控 制台显示状态信息。在 Main 子例程中创建名为 rp 的 ReportPrinter 类的实例,并调用其 PrintNextReport 方法两次以生成两个报告。将两个参数传递给该方法。第一个参数为新的 指向调用应用程序的 GetPrintInfo 方法的 PrintInfoCallBack 委托。第二个参数为包含状态 的对象,该参数将被传递回回调方法。由于该状态将被传递回回调方法,因此可为应用程序 想要跟踪的任何信息。 当控制台应用程序启动时,Main 方法创建 ReportPrinter 类的实例并调用 ReportPrinter.PrintNextReport 方法。ReportPrinter 类回调控制台应用程序,这是因为 ReportPrinter.PrintNextReport 方法调用控制台应用程序的 GetPrintInfo 方法。 [C#] class Program { static void GetPrintInfo(PrintStatus Status, object State) { Console.WriteLine("{0} Print Status = {1}", State, Status); } 4
static void Main(string[] args) { ReportPrinter rp = new ReportPrinter(); rp.PrintNextReport(new PrintInfoCallBack(Program.GetPrintInfo), "First rp.PrintNextReport(new PrintInfoCallBack(Program.GetPrintInfo), "Second Report"); Report"); } } [VB.NET] Class Program Shared Sub GetPrintInfo(ByVal Status As PrintStatus, _ ByVal State As Object) Console.WriteLine("{0} Print Status = {1}", State, Status) End Sub Shared Sub Main(ByVal args As String()) Dim rp As ReportPrinter = New ReportPrinter rp.PrintNextReport(AddressOf GetPrintInfo, "First Report") rp.PrintNextReport(AddressOf GetPrintInfo, "Second Report") End Sub End Class 表 4- 1 描述了 Delegate 类的成员。 表 4- 1 成员 Method 属性 Target 属性 描述 返回委托引用的方法的只读属性。如果委托引用了多个方法,则 Method 属性 将返回委托的调用列表中的最后一个方法。 返回包含委托引用的方法的对象的只读属性。如果方法是静态的,则返回的值为 null。如果委托引用了多个方法,则 Target 属性将返回包含调用列表中最后一 个方法的对象。 Combine 方法 将委托数组的调用列表串连在一起的静态方法。可使用该方法将多个委托的调用 CreateDelegate 方法 DynamicInvoke 方法 列表组合到一个委托中。 可用来创建引用实例或静态(VB.NET 中为共享)方法的委托的静态方法。 该方法调用委托的调用列表中的方法。被调用的方法的参数以数组的形式传递。 如果委托不使用参数,则参数数组应为 Null。 GetInvocationList 方法 该方法返回一个单路广播委托数组,表示当前委托的调用列表。如果委托只引用 一个方法,则该数组将仅包含一个引用其自身的元素。如果委托为多路广播,则 该数组包含多个元素。 Remove 方法 RemoveAll 方法 将指定委托的调用列表的最后一个实例从另一个委托中移除的静态方法。 将指定委托的调用列表的所有实例从另一个委托中移除的静态方法。 在以下代码示例中,修改 Program 类以将多路广播委托传递给 PrintNextReport 方法。创 建两个方法 GetPrintInfo1 和 GetPrintInfo2。创建名为 picb 的多路广播委托。这个委托将被 5
传递给 PrintNextReport 方法,并且按照方法被添加到委托中的顺序依次调用这些方法。在 这种情况下,首先调用 GetPrintInfo1,然后调用 GetPrintInfo2。注意 C#允许使用加法复合 赋值(+=)运算符将两个委托合并,而 VB.NET 则要求使用委托类型的 Combine 方法才能 达到同样目的。 [C#] class Program { static void GetPrintInfo1(PrintStatus Status, object State) { Console.WriteLine("{0} Print Status " + "1 = {1}", State, Status); } static void GetPrintInfo2(PrintStatus Status, object State) { Console.WriteLine("{0} Print Status " + "2 = {1}", State, Status); } static void Main(string[] args) { ReportPrinter rp = new ReportPrinter(); PrintInfoCallBack picb = new PrintInfoCallBack(Program.GetPrintInfo1); picb += new PrintInfoCallBack(Program.GetPrintInfo2); rp.PrintNextReport(picb, "First Report"); rp.PrintNextReport(picb, "Second Report"); } } [VB.NET] Class Program Shared Sub GetPrintInfo1(ByVal Status _ As PrintStatus, ByVal State As Object) Console.WriteLine("{0} Print " & _ "Status 1 = {1}", State, Status) End Sub Shared Sub GetPrintInfo2(ByVal Status _ As PrintStatus, ByVal State As Object) Console.WriteLine("{0} Print " & _ "Status 2 = {1}", State, Status) End Sub Shared Sub Main(ByVal args As String()) Dim rp As ReportPrinter = New _ ReportPrinter 6
Dim picb1 As New PrintInfoCallBack _ (AddressOf GetPrintInfo1) Dim picb2 As New PrintInfoCallBack _ (AddressOf GetPrintInfo2) Dim picb As PrintInfoCallBack = _ PrintInfoCallBack.Combine(picb1, picb2) rp.PrintNextReport(picb, _ "First Report") rp.PrintNextReport(picb, _ "Second Report") End Sub End Class 以下代码示例给出了当实例化委托时使用泛型委托声明来动态指派委托的返回类型的 过程。在该示例中,声明了一个包含星期中每一天的名为 days 的枚举。然后,声明了一个 名为 GetDay 的泛型委托声明。GetDay 委托定义一个 T 返回类型。在创建 GetDay 委托 的新实例时指定返回类型。在 Program 类中有两个函数。第一个函数将 days 枚举作为一个 参数并返回一个整数,而第二个函数返回一个字符串。在 Main 方法中创建并实例化了两 个 GetDay 类型的委托,分别为 Position 和 Name。当声明这些委托时,指定 Name 将返 回字符串,Position 将返回整数。调用每个委托并将输出写入命令控制台。注意调用 Position 返回一个整数,而调用 Name 返回一个字符串。 [C#] public enum days { Sun = 1, Mon, Tue, Wed, Thu, Fri, Sat } public delegate T GetDay(days Day); class Program { public static int GetDayPosition(days Day) { return (int)Day; } public static string GetDayName(days Day) { return Day.ToString(); 7
} static void Main(string[] args) { GetDay Position = GetDayPosition; GetDay Name = GetDayName; Console.WriteLine(Position(days.Fri)); Console.WriteLine(Name(days.Fri)); } } [VB.NET] Public Enum days Sun = 1 Mon Tue Wed Thu Fri Sat End Enum Delegate Function GetDay(Of T)(ByVal _ Day As days) As T Class Program Public Shared Function _ GetDayPosition(ByVal Day As days) As _ Integer Return CType(Day, Integer) End Function Public Shared Function GetDayName _ (ByVal Day As days) As String Return Day.ToString() End Function Shared Sub Main() Dim Position As New GetDay _ (Of Integer)(AddressOf GetDayPosition) Dim Name As New GetDay _ (Of String)(AddressOf GetDayName) Console.WriteLine(Position(days.Fri)) Console.WriteLine(Name(days.Fri)) End Sub End Class 8
分享到:
收藏