理解委托的一个例子

来自 Learn hard C# 学习笔记 这本书的例子。

委托

C# 中的委托可以理解为函数的一个包装,它使得 C# 中的函数可以作为参数来被传递。

委托的定义和方法的定义类似,只是在定义的前面多了一个 delegate 关键字。即

public delegate void MyDelegate(int para1, string para2);

委托与方法

class Program
{
    static void Main(string[] args)
    {
        Program p = new Program();
        p.Greeting("鹿晗", p.ChineseGreeting);
        p.Greeting("Tommy Li", p.EnglishGreeting);
        Console.Read();
    }
    // 定义委托类型
    public delegate void GreetingDelegate(string name); 
    
    //有了委托之后,可以像下面这样实现打招呼方法
    public void Greeting(string name, GreetingDelegate callback)
    {
        //调用委托
        callback(name);    
    }
    //英国人打招呼方法
    public void EnglishGreeting(string name)
    {
        Console.WriteLine("Hello," + name);    
    }
    //中国人打招呼方法
    public void ChineseGreeting(string name)
    {
        Console.WriteLine("你好," + name);    
    }
}

主要是在 p.Greeting 这个方法中,将一个方法传递给一个委托类 GreetingDelegate,这个委托来直接调用。

即委托的作用:将函数作为参数传递给另一个方法。

委托的本质:

这句代码 callback(name); 实际上是隐式调用了 Invoke方法。

即 IL 代码为 :callback.Invoke(name);

所以你可以看到,委托是类类型,委托实例可以调用实例方法。

事件

事件是成员。An envent is a member.

和方法、属性一样,事件是类或结构的成员。(有点类似于类中的属性)

“事件” 存在的首要理由和 “属性” 差不多--它们添加了一个封装层,实现发布/订购模式(publish/subscribe pattern)。

例:当声明了一个事件。

// 自定义了一个委托(这是类,可以与类平行)
public delegate void MarryHandler(string msg);  


// 使用自定义委托定义事件 (这相当于成员,只能在类里面定义)
public event MarryHandler MarryEvent;           

从 IL 可以看出,MarryHandler 委托被编译成一个名为 MarryHandler 的类,而 MarryEvent 事件则被编译成如下所示的 IL 代码

.event EventDemo.Program/MarryHandler MarryEvent
{
    .addon instance void EventDemo.Program::add_MarryEvent(class EventDemo.Program/MarryHandler)
    .removeon instance void EventDemo.Program::remove_MarryEvent(class EventDemo.Program/MarryHandler)
} // end of event Program::MarryEvent

从IL代码可以看出,C#事件被编译成包含两个公共方法的代码段,一个带有add_ 前缀,另一个带有 remove_ 前缀,前缀后面是C#事件的名称。

查看 add_MarryEvent() 的IL代码,就会发现该方法是通过 调用 Delegate.Combine() 方法来实现的。相应地,remove_MarryEvent 方法也将间接调用Delegate.Remove()方法。

.method public hidebysig specialname instance void 
        add_MarryEvent(class EventDemo.Program/MarryHandler 'value') cil managed
{
  ...
  IL_000b:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
                                                                                          class [mscorlib]System.Delegate)
  ...
} // end of method Program::add_MarryEvent

同时,还有一个 MarryEvent 字段的定义。MarryEvent : private class EventDemo.Program/MarryHandler。查看这句 IL 代码发现这是一个 field。

.field private class EventDemo.Program/MarryHandler MarryEvent

从前面的分析可以得出,C# 中的事件是一个特殊的多路广播委托,事件默认含有一个私有的委托类型变量,该变量用于保存对事件处理方法的引用,且该委托类型的变量为私有,只能从定义该事件的类中进行访问。

由事件的IL代码段,我们可以联想到类中属性的定义。与事件不同,属性中定义了 set 访间器和 get 访问器,两个访问器的本质是以 “get” 和 “set” 为前缀的两个方法。属性用于对类中的私有字段进行访问,而 C# 事件也可以看作是“委托字段的属性”,因此可以通过事件来对私有的委托字段进行访问,这也是 C# 事件特性存在的原因。C# 事件机制符合面向对象的封装特性它使得应用程序代码更加安全。

其他解释(翻译文体):

(通常,我们不希望其他代码能直接设置字段值;最起码也要先由所有者(owner)对新值进行验证。同样,我们通常不希望类外部的代码随意更改(或调用)一个事件的处理程序)。(感觉就是封装的意思)

字段风格的事件使所有这些的实现变得更易阅读 -- 只需一个声明就可以了。编译器会将声明转换成一个具有默认add/remove实现的事件和一个私有委托类型的字段。类内的代码能看见字段;类外的代码只能看见事件。这样一来,表面上似乎能调用一个事件,但为了调用事件处理程序,实际做的事情是调用存储在字段中的委托实例。

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值