委托(Delegate)的初学者指南

在本文中,将简要介绍委托(Delegate)的概念。这可以作为.NET编程中委托使用的入门指南。

在开始之前,假设已经具备了函数指针的基础知识。因为在介绍部分,将委托与函数指针进行比较。如果对函数指针还不太了解,可以参考。

简单委托

委托非常类似于C/C++中的“函数指针”或“typedef”,它代表了函数的地址。在C/C++中,函数的地址仅仅是一个内存地址。函数指针只持有函数的地址,而不包含任何额外的信息,比如函数期望的参数数量、参数类型等。实际上,这使得函数指针类型不安全。传统上,调用函数指针依赖于语言对函数指针的支持,而函数指针本身是危险的。

委托为传统的函数指针概念增加了安全性。.NET框架增加了一种类型安全的机制,称为委托。它们遵循“信任但验证”的模型,编译器自动验证签名。与函数指针不同,委托是面向对象的、类型安全的、安全的。简而言之,委托是一种数据结构,它引用静态方法或对象实例及其实例方法。当委托引用实例方法时,它不仅存储方法入口点的引用,还存储用于调用该方法的对象实例的引用。

在VB.NET中,使用“Delegate”关键字定义委托。例如: Public Delegate Sub GreetingDelegate(ByVal MsgString As String) 这行代码并没有声明委托,而是定义了委托。通过这行代码,告诉编译器创建一个名为“GreetingDelegate”的新类,该类继承自“System.Delegate”(编译器会为自动完成)。这行代码等同于在C/C++中编写以下代码: typedef void (*GreetingDelegate) (char *); 这个委托只能调用一个接受“String”参数且不返回任何内容的方法。委托只关心封装在其中的方法的签名。因此,可以安全地使用这个委托来调用具有适当签名的函数。

在这里,写了两个不同的VB.NET函数: Public Sub GoodMorning(ByVal YourName As String) Console.WriteLine("Good Morning " + YourName + "!") End Sub Public Sub GoodNight(ByVal YourName As String) Console.WriteLine("Good Night " + YourName + "!") End Sub 现在,在Sub Main中,可以创建委托的实例,并将函数的地址分配给委托。这是使用AddressOf关键字完成的: Dim MyGreeting As GreetingDelegate Console.WriteLine("Adding 'GoodMorning' Reference To A Delegate...") MyGreeting = AddressOf GoodMorning 一旦给委托分配了地址,可以使用Invoke方法来调用封装在委托中的函数。 Console.WriteLine("Invoking Delegate...") MyGreeting.Invoke("Mallinath") 因为委托只关心封装在其中的方法的签名,所以可以让它引用另一个方法。例如: Console.WriteLine("Making Existing Delegate To Point To Another Function...") Console.WriteLine("Replacing With Goodnight Reference...") MyGreeting = New GreetingDelegate(AddressOf Goodnight) 另一种调用委托的方式。 Console.WriteLine("Invoking Delegate...") MyGreeting("Mallinath") 惊讶吗?忘记放Invoke()了吗?不!实际上,不需要使用Invoke(),可以直接使用委托作为方法的代理。

如果尝试分配一个与委托声明不匹配的方法或函数的地址,将得到一个编译器错误。尝试以下代码,将其放入下载的源代码中: Public Sub BadGreets() Console.WriteLine("Wishing You Bad Greetings !") End Sub Sub Main() Dim MyGreeting As GreetingDelegate MyGreeting = AddressOf BadGreets MyGreeting.Invoke("Mallinath") End Sub

接下来是多播委托,这些委托能够指向具有相同签名的多个函数。System.Delegate类型维护一个链接列表,用于保存委托要调用的函数的引用。这个链接列表被称为调用列表(Invocation List)。委托类提供了一个共享成员函数Combine(),它将指定函数的引用添加到调用列表中。

为了演示多播委托,在MulticastDelegate示例程序中添加了一个新的函数'GoodEvening',以及之前的两个函数。为之前的每个函数创建了单独的委托。 Public Delegate Sub GreetingDelegate(ByVal MsgString As String) Public Sub GoodMorning(ByVal YourName As String) Console.WriteLine("Good Morning " + YourName + "!") End Sub Public Sub Goodnight(ByVal YourName As String) Console.WriteLine("Good Night " + YourName + "!") End Sub Public Sub GoodEvening(ByVal YourName As String) Console.WriteLine("Good Evening " + YourName + "!") End Sub 在Sub Main中,实例化它们如下: Dim MorningGreets As GreetingDelegate Dim EveningGreets As GreetingDelegate MorningGreets = AddressOf GoodMorning EveningGreets = New GreetingDelegate(AddressOf GoodEvening) 为了说明多播委托,将两个委托合并为一个委托,使用Combine()方法: Console.WriteLine("Adding 'MorningGreets' And 'EveningGreets' References To A Delegate...") Dim AllGreets As GreetingDelegate = Delegate.Combine(MorningGreets, EveningGreets) Combine()返回一个新委托,合并了指定委托的函数引用的调用列表。在这里,可以向现有委托添加另一个函数引用: Console.WriteLine("Adding Another Reference To Existing Delegate...") AllGreets = Delegate.Combine(AllGreets, New GreetingDelegate(AddressOf Goodnight)) 正如写的,委托定义行告诉编译器创建一个继承自System.Delegate类型的新类: Dim NightGreets As GreetingDelegate = AddressOf Goodnight 这等同于: Dim NightGreets As GreetingDelegate NightGreets = New GreetingDelegate(AddressOf Goodnight) 所以,整个事情可以总结为: AllGreets = Delegate.Combine(AllGreets, New GreetingDelegate(AddressOf Goodnight)) 就像函数一样,要向委托的调用列表添加函数引用,委托类提供了一个函数来从委托调用列表中移除函数引用。 例如: Console.WriteLine("Removing 'GoodEvening' Reference...") AllGreets = Delegate.Remove(AllGreets, EveningGreets) 对多播委托的Invoke函数调用将调用存储在该委托对象调用列表中的所有函数。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485