C#C# 中的委托和事件

PDF
浏览:http://www.tracefact.net/Document/Delegates-and-Events-in-CSharp.pdf

文中代码在VS二零零六下通过,由于VS200三(.Net
Framework
一.一)不协助隐式的委托变量,所以借使在3个收受委托项指标位置一向给予方法名,在VS2003下会报错,化解办法是显式的始建一个信托项指标实例(委托变量)。例如:委托项目
委托实例 = new 委托类型(方法名); 

欢迎浏览本文的继承作品: C#中的委托和事件(续)

源码下载:http://www.tracefact.net/SourceCode/Delegates-and-Events-in-CSharp.rar

C# 中的委托和事件

引言

委托 和 事件在 .Net
Framework中的应用格外普遍,可是,较好地驾驭委托和事件对许多触及C#岁月不短的人的话并不易于。它们就像1道槛儿,过了这几个槛的人,觉得真是太轻巧了,而并未有过去的人每一遍观望委托和事件就觉得心里别(biè)得慌,混身不自在。本文中,小编将由此五个范例由表及里地叙述什么是委托、为何要利用委托、事件的来头、.Net
Framework中的委托和事件、委托和事件对Observer设计格局的意义,对它们的中间代码也做了研讨。

将艺术作为艺术的参数

咱俩先不管这几个标题怎么样的绕口,也不论委托毕竟是个什么东西,来看下边那三个最简便易行的方法,它们只是是在荧屏上输出一句问候的口舌:

public void GreetPeople(string name) {
    //
做壹些额外的业务,比如发轫化之类,此处略
    EnglishGreeting(name);
}
public void EnglishGreeting(string name) {
    Console.WriteLine(“Morning,
” + name);
}

姑且不论那多个措施有未有哪些实际意义。GreetPeople用于向某人问好,当大家传递代表某人姓名的name参数,比如说“吉姆my”,进去的时候,在这一个方法中,将调用EnglishGreeting方法,再度传递name参数,EnglishGreeting则用来向显示屏输出
“Morning, 吉米my”。

今昔只要这些顺序供给举行满世界化,哎哎,不佳了,我是中夏族,作者不驾驭“Morning”是怎么着意思,如何做呢?好啊,大家再加个普通话版的问候方法:

public void ChineseGreeting(string name){
    Console.WriteLine(“早上好,
” + name);
}

这时候,GreetPeople也急需改一改了,不然怎么判断终归用哪些版本的Greeting问候方法妥帖呢?在拓展那个此前,我们最棒再定义三个枚举作为判断的依照:

public enum Language{
    English, Chinese
}

public void GreetPeople(string name, Language lang){
    //做某个额外的政工,比如起头化之类,此处略
    swith(lang){
        case Language.English:
           EnglishGreeting(name);
           break;
       case Language.Chinese:
           ChineseGreeting(name);
           break;
    }
}

OK,固然那样解决了难点,但作者不说大家也很轻巧想到,这些化解方案的可扩张性很差,要是将来我们须要再增添意大利语版、日文版,就只可以壹再修改枚举和GreetPeople()方法,以适应新的需要。

在设想新的缓解方案在此之前,大家先看看 GreetPeople的艺术签字:

public void GreetPeople(string name, Language lang)

咱俩仅看 string name,在此处,string 是参数类型,name
是参数变量,当我们赋给name字符串“jimmy”时,它就象征“jimmy”那几个值;当大家赋给它“张子阳”时,它又意味着着“张子阳”这些值。然后,大家能够在方式体内对这一个name举办任何操作。哎,这简直是废话么,刚学程序就精晓了。

借使您再仔细考虑,尽管GreetPeople()方法勉强接受一个参数变量,那些变量能够代表另二个方法,当大家给那个变量赋值
EnglishGreeting的时候,它意味着着 EnglsihGreeting()
这么些方法;当我们给它赋值ChineseGreeting
的时候,它又象征着ChineseGreeting()方法。大家将以此参数变量命名字为MakeGreeting,那么不是足以犹如给name赋值时1致,在调用
GreetPeople()方法的时候,给那几个MakeGreeting
参数也赋上值么(ChineseGreeting可能EnglsihGreeting等)?然后,我们在章程体内,也足以像使用别的参数一样使用MakeGreeting。但是,由于MakeGreeting代表着一个方式,它的使用方法应该和它被赋的秘籍(比如ChineseGreeting)是一致的,比如:

MakeGreeting(name);

好了,有了思路了,大家前些天就来改改GreetPeople()方法,那么它应有是以此样子了:

public void GreetPeople(string name, *** MakeGreeting){
    MakeGreeting(name);
}

注意到 ***
,那个岗位1般放置的应有是参数的品类,但到近日甘休,大家仅仅是想到应该有个能够象征办法的参数,并按这些思路去改写GreetPeople方法,未来就涌出了二个大难点:其一象征着办法的MakeGreeting参数应该是什么样类型的?

NOTE:那边已不再要求枚举了,因为在给MakeGreeting赋值的时候动态地操纵使用哪个方法,是ChineseGreeting依然EnglishGreeting,而在这么些多少个章程内部,已经对选择“morning”仍然“中午好”作了界别。

智慧的您应当早就想到了,今后是委托该出场的时候了,但讲述委托在此以前,大家再看看MakeGreeting参数所能代表的
ChineseGreeting()和EnglishGreeting()方法的签订契约:

public void EnglishGreeting(string name)
public void ChineseGreeting(string name)

就像name能够承受String类型的“true”和“一”,但无法接受bool类型的true和int类型的1同等。MakeGreeting的
参数类型定义 应该能够规定
MakeGreeting能够象征的方法种类,再进一步讲,就是MakeGreeting能够代表的法子
的 参数类型和再次回到类型。

于是,委托现身了:它定义了MakeGreeting参数所能代表的不2秘籍的门类,也便是MakeGreeting参数的项目。

NOTE:1旦上边那句话相比较绕口,小编把它翻译成那样:string
定义了name参数所能代表的值的门类,也等于name参数的品种。

本例中央委员托的概念:

public delegate void GreetingDelegate(string name);

能够与地点EnglishGreeting()方法的签字相比较一下,除了加入了delegate关键字以外,别的的是还是不是一心平等?

现行反革命,让我们再度改变GreetPeople()方法,如下所示:

public void GreetPeople(string name, GreetingDelegate
MakeGreeting){
    MakeGreeting(name);
}

如你所见,委托GreetingDelegate出现的岗位与
string相同,string是2个档次,那么GreetingDelegate应该也是一个连串,恐怕叫类(Class)。不过委托的注解格局和类却全然不一样,那是怎么3遍事?实际上,委托在编写翻译的时候确实会编译成类。因为Delegate是一个类,所以在别的能够注明类的地点都足以表明委托。越来越多的始末将在上面讲述,现在,请看看这几个范例的完整代码:

using System;
using System.Collections.Generic;
using System.Text;

namespace Delegate {
     //定义委托,它定义了能够表示的不二等秘书籍的门类
     public delegate void GreetingDelegate(string name);
        class Program {

           private static void EnglishGreeting(string name) {
               Console.WriteLine(“Morning, ” + name);
           }

           private static void ChineseGreeting(string name) {
               Console.WriteLine(“早上好, ” + name);
           }

           //注意此办法,它接受三个GreetingDelegate类型的办法作为参数
           private static void GreetPeople(string name, GreetingDelegate
MakeGreeting) {
               MakeGreeting(name);
            }

           static void Main(string[] args) {
               GreetPeople(“Jimmy Zhang”, EnglishGreeting);
               GreetPeople(“张子阳”, ChineseGreeting);
               Console.ReadKey();
           }
        }
    }

输出如下:
Morning, Jimmy Zhang
早上好, 张子阳

笔者们今后对信托做1个总括:

信托是贰个类,它定义了法子的类型,使得能够将艺术当作另三个办法的参数来进展传递,那种将艺术动态地赋给参数的做法,可防止止在先后中山大学量使用If-Else(Switch)语句,同时使得程序有所更加好的可扩张性。

将艺术绑定到委托

旁观此间,是或不是有那么点如梦初醒的感到?于是,你是还是不是在想:在上头的例证中,我不肯定要直接在GreetPeople()方法中给
name参数赋值,作者能够像那样使用变量:

static void Main(string[]
args) {
    string name1, name2;
    name1 = “Jimmy
Zhang”;
    name2 = “张子阳”; 

     GreetPeople(name1, EnglishGreeting);
     GreetPeople(name2, ChineseGreeting);
    Console.ReadKey();
}

而既然委托GreetingDelegate 和 类型 string
的地位平等,都以概念了壹种参数类型,那么,小编是否也足以如此使用委托?

static void Main(string[]
args) {
    GreetingDelegate delegate1, delegate2;
    delegate1 = EnglishGreeting;
    delegate2 = ChineseGreeting;

    GreetPeople(“Jimmy Zhang”,
delegate1);
        GreetPeople(“张子阳”,
delegate2);
        Console.ReadKey();
}

如您所料,那样是向来不难题的,程序1如预期的那样输出。那里,作者想说的是信托分化于string的二个风味:可以将五个办法赋给同三个委托,只怕叫将八个主意绑定到同3个寄托,当调用那些委托的时候,将相继调用其所绑定的艺术。在那一个例子中,语法如下:

static void Main(string[]
args) {
    GreetingDelegate delegate1;
    delegate一 = EnglishGreeting; //
先给委托项指标变量赋值
    delegate一 += ChineseGreeting;   //
给此委托变量再绑定1个主意

     // 将次第调用
EnglishGreeting 与 ChineseGreeting 方法
    GreetPeople(“Jimmy Zhang”,
delegate1);  
    Console.ReadKey();
}

输出为:
Morning, Jimmy Zhang
早上好, Jimmy Zhang

实则,大家能够也能够绕过GreetPeople方法,通过信托来平素调用EnglishGreeting和ChineseGreeting:

static void Main(string[]
args) {
    GreetingDelegate delegate1;
    delegate壹 = EnglishGreeting; //
先给委托项指标变量赋值
    delegate一 += ChineseGreeting;   //
给此委托变量再绑定3个措施

    // 将先后调用
EnglishGreeting 与 ChineseGreeting 方法
    delegate1 (“Jimmy
Zhang”);   
    Console.ReadKey();
}

 

NOTE:那在本例中是不曾问题的,但悔过看下上边GreetPeople()的定义,在它当中能够做1些对此EnglshihGreeting和ChineseGreeting来说都亟待开展的劳作,为了方便小编做了简短。

专注那里,第3回用的“=”,是赋值的语法;第一次,用的是“+=”,是绑定的语法。如若第二回就应用“+=”,将面世“使用了未赋值的局地变量”的编写翻译错误。

咱俩也得以采用下边包车型地铁代码来如此简化那一进度:

GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
delegate一 += ChineseGreeting;   // 给此委托变量再绑定二个办法

见状那里,应该注意到,那段代码第3条语句与实例化三个类是何其的相似,你禁不住想到:上面第3次绑定委托时不能动用“+=”的编写翻译错误,也许能够用这么的办法来防止:

GreetingDelegate delegate1 = new GreetingDelegate();
delegate1 += EnglishGreeting;   // 本次用的是
“+=”,绑定语法。
delegate壹 += ChineseGreeting;   // 给此委托变量再绑定1个主意

但实在,那样会产出编译错误:
“GreetingDelegate”方法未有运用“0”个参数的重载。尽管那样的结果让我们认为有点消极,可是编写翻译的升迁:“未有0个参数的重载”再度让我们联想到了类的构造函数。笔者通晓您一定迫在眉睫想探个究竟,但再此以前,大家必要先把基础知识和平运动用介绍完。

既然给委托能够绑定1个办法,那么也应该有方法撤除对章程的绑定,很轻易想到,这几个语法是“-=”:

static void Main(string[]
args) {
    GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);
    delegate1 += ChineseGreeting;   //
给此委托变量再绑定一个主意

    // 将先后调用
EnglishGreeting 与 ChineseGreeting 方法
    GreetPeople(“Jimmy Zhang”,
delegate1);  
    Console.WriteLine();

    delegate壹 -= EnglishGreeting; //撤废对EnglishGreeting方法的绑定
    // 将仅调用
ChineseGreeting 
    GreetPeople(“张子阳”,
delegate1); 
    Console.ReadKey();
}
输出为:
Morning, Jimmy Zhang
早上好, Jimmy Zhang
早上好, 张子阳

让大家再一次对信托作个总计:

利用委托能够将七个点子绑定到同1个信托变量,当调用此变量时(这里用“调用”这几个词,是因为此变量代表3个措施),能够依次调用全部绑定的措施。

事件的案由

咱俩继续思量上边的程序:下面的多个方法都定义在Programe类中,那样做是为着知道的有益,实际行使中,日常都以GreetPeople 在1个类中,ChineseGreeting和 EnglishGreeting
在其余的类中。将来您曾经对信托有了初叶通晓,是时候对上边的事例做个创新了。假诺大家将GreetingPeople()放在三个叫GreetingManager的类中,那么新程序应该是以此样子的:

namespace Delegate {
    //定义委托,它定义了足以代表的方式的品种
    public delegate void GreetingDelegate(string name);
    
    //新建的GreetingManager类
    public class GreetingManager{
       public void GreetPeople(string name, GreetingDelegate
MakeGreeting) {
           MakeGreeting(name);
       }
    }

    class Program {
       private static void EnglishGreeting(string name) {
           Console.WriteLine(“Morning, ” + name);
       }

       private static void ChineseGreeting(string name) {
           Console.WriteLine(“早上好, ” + name);
       }

       static void Main(string[] args) {
           // … …
        }
    }
}

以此时候,假如要兑现前边演示的输出效果,Main方法本人想应该是这么的:

static void Main(string[]
args) {
    GreetingManager gm = new  GreetingManager();
    gm.GreetPeople(“Jimmy Zhang”,
EnglishGreeting);
    gm.GreetPeople(“张子阳”,
ChineseGreeting);
}

大家启动那段代码,嗯,未有别的难题。程序1如预期地那么输出了:

Morning, Jimmy Zhang

早上好, 张子阳

到现在,就算大家供给动用上壹节学到的知识,将多少个章程绑定到同一个寄托变量,该怎么办吧?让大家再度改写代码:

static void Main(string[]
args) {
    GreetingManager gm = new 
GreetingManager();
    GreetingDelegate delegate1;
    delegate1 = EnglishGreeting;
    delegate1 += ChineseGreeting;

    gm.GreetPeople(“Jimmy Zhang”,
delegate1);
}

输出:
Morning, Jimmy Zhang
早上好, Jimmy Zhang

到了此间,大家不禁想到:面向对象设计,讲究的是目的的包裹,既然能够评释委托项指标变量(在上例中是delegate壹),大家何不将以此变量封装到
GreetManager类中?在这几个类的客户端中动用不是更方便么?于是,我们改写GreetManager类,像那样:

public class GreetingManager{
    //在GreetingManager类的其中宣称delegate一变量
    public GreetingDelegate delegate1;  

    public void GreetPeople(string name, GreetingDelegate
MakeGreeting) {
       MakeGreeting(name);
    }
}

未来,大家得以这么使用那些委托变量:

static void Main(string[]
args) {
    GreetingManager gm = new 
GreetingManager();
    gm.delegate1 = EnglishGreeting;
    gm.delegate1 += ChineseGreeting;

    gm.GreetPeople(“Jimmy Zhang”,
gm.delegate1);
}

输出为:
Morning, Jimmy Zhang
早上好, Jimmy Zhang

固然那样做未有其余难点,但大家发现那条语句很离奇。在调用gm.GreetPeople方法的时候,再一次传递了gm的delegate一字段:

gm.GreetPeople(“Jimmy Zhang”,
gm.delegate1);

既是,我们何不更动 GreetingManager 类成这么:

public class GreetingManager{
    //在GreetingManager类的个中宣称delegate壹变量
    public GreetingDelegate delegate1;  

    public void GreetPeople(string name) {
        if(delegate1!=null){     //假诺有方法注册委托变量
          delegate一(name);      //通过委托调用方法
       }
    }
}

在客户端,调用看上去更简短一些:

static void Main(string[]
args) {
    GreetingManager gm = new 
GreetingManager();
    gm.delegate1 = EnglishGreeting;
    gm.delegate1 += ChineseGreeting;

    gm.GreetPeople(“吉姆my
Zhang”);      //注意,本次不须求再传递
delegate一变量
}

输出为:
Morning, Jimmy Zhang
早上好, Jimmy Zhang

尽管那样达到了大家要的职能,然而依旧存在着难点:

在此处,delegate一和大家日常用的string类型的变量未有啥分别,而笔者辈精晓,并不是兼备的字段都应有表明成public,合适的做法是理所应当public的时候public,应该private的时候private。

大家先看看假使把 delegate一 注解为
private会怎么样?结果正是:这大概便是在搞笑。因为宣称委托的目标就是为了把它暴光在类的客户端实行艺术的挂号,你把它注明为private了,客户端对它根本就不可知,那它还有何样用?

再看看把delegate1 注解为 public
会怎么着?结果就是:在客户端可以对它实行随机的赋值等操作,严重破坏对象的封装性。

最终,第2个主意注册用“=”,是赋值语法,因为要拓展实例化,第3个措施注册则用的是“+=”。不过,不管是赋值照旧注册,都以将艺术绑定到委托上,除了调用时先后顺序不相同,再未有别的的分别,那样不是令人以为很别扭么?

今后我们思索,如若delegate1不是2个信托项目,而是1个string类型,你会怎么办?答案是使用性质对字段实行李包裹装。

于是乎,伊夫nt出场了,它包裹了委托项目标变量,使得:在类的内部,不管您评释它是public还是protected,它连接private的。在类的表面,注册“+=”和撤废“-=”的拜访限定符与你在宣称事件时选用的造访符相同。

笔者们改写GreetingManager类,它变成了这么些样子:

public class GreetingManager{
    //那一次大家在那边声Bellamy个轩然大波
    public event GreetingDelegate MakeGreet;

    public void GreetPeople(string name) {
        MakeGreet(name);
    }
}

很轻巧注意到:MakeGreet
事件的宣示与事先委托变量delegate一的扬言唯一的界别是多了三个event关键字。看到那里,在组合地方的授课,你应当懂获得:事件实际上没什么倒霉明白的,声澳优个轩然大波只是类似于声美素佳儿个人作品张开了包装的嘱托项目标变量而已。

为了印证上边的猜度,假如咱们像下边那样改写Main方法:

static void Main(string[]
args) {
    GreetingManager gm = new 
GreetingManager();
    gm.MakeGreet = EnglishGreeting;         // 编写翻译错误一
    gm.MakeGreet += ChineseGreeting;

    gm.GreetPeople(“Jimmy
Zhang”);
}

会收获编写翻译错误:事件“Delegate.GreetingManager.MakeGreet”只可以冒出在 += 或
-= 的右边(从品种“Delegate.GreetingManager”中应用时除了)。

事件和委托的编写翻译代码

此刻,我们诠释掉编写翻译错误的行,然后重新举行编译,再借助Reflactor来对
event的扬言语句做一查究,看看为啥会发生那样的谬误:

public event GreetingDelegate MakeGreet;

C# 1

能够看出,实际上纵然大家在GreetingManager里将 MakeGreet
表明为public,可是,实际上MakeGreet会被编写翻译成
私有字段,难怪会发生下边包车型大巴编写翻译错误了,因为它根本就不容许在GreetingManager类的外围以赋值的章程访问,从而证实了我们地点所做的推理。

作者们再进一步看下MakeGreet所发出的代码:

private GreetingDelegate MakeGreet; //对事件的宣示 实际是
注解一(Wissu)个私家的寄托变量
 
[MethodImpl(MethodImplOptions.Synchronized)]
public void add_MakeGreet(GreetingDelegate
value){
    this.MakeGreet
= (GreetingDelegate) Delegate.Combine(this.MakeGreet,
value);
}

[MethodImpl(MethodImplOptions.Synchronized)]
public void remove_MakeGreet(GreetingDelegate
value){
    this.MakeGreet
= (GreetingDelegate) Delegate.Remove(this.MakeGreet,
value);
}

现行反革命早已很醒目了:MakeGreet事件真的是二个GreetingDelegate类型的寄托,只可是不管是或不是宣称为public,它连接被声称为private。其它,它还有七个章程,分别是add_MakeGreet和remove_MakeGreet,那八个法子分别用于注册委托项指标方法和打消注册。骨子里约等于:
“+= ”对应
add_MakeGreet,“-=”对应remove_MakeGreet。而那多个方法的拜会限制在于申明事件时的造访限制符。

在add_MakeGreet()方法内部,实际上调用了System.Delegate的Combine()静态方法,这几个情势用于将如今的变量增加到委托链表中。我们前边提到过一遍,说委托实际上是2个类,在大家定义委托的时候:

public delegate void GreetingDelegate(string name);

当编写翻译器境遇那段代码的时候,会变动下边这样二个1体化的类:

public sealed class GreetingDelegate:System.MulticastDelegate{
    public GreetingDelegate(object @object, IntPtr
method);
    public virtual IAsyncResult BeginInvoke(string name, AsyncCallback callback, object @object);
    public virtual void EndInvoke(IAsyncResult result);
    public virtual void Invoke(string name);
}

 

C# 2

 

有关那一个类的更加尖锐内容,能够参见《CL卡宴 Via
C#》等相关书籍,那里就不再斟酌了。

寄托、事件与Observer设计格局

范例表明

地点的事例已不足以再张开上面包车型地铁上课了,大家来看四个新的范例,因为此前已经介绍了过多的剧情,所以本节的进程会略带快一些:

1旦我们有个高级的磁能热水器,大家给它通上电,当水温超越九5度的时候:1、扬声器会伊始发出语音,告诉您水的热度;二、液晶屏也会变动水温的来得,来提示水已经快烧开了。

前些天大家必要写个程序来模拟那个烧水的经过,大家将定义二个类来表示热水器,大家管它叫:Heater,它有象征水温的字段,叫做temperature;当然,还有须要的给水加热方法Boil沃特er(),二个发生语音警报的格局MakeAlert(),一个来得水温的方法,ShowMsg()。

namespace Delegate {
    class Heater {
    private int temperature; // 水温
    // 烧水
    public void BoilWater() {
        for (int i = 0; i <= 100; i++) {
           temperature = i;

           if (temperature > 95) {
               MakeAlert(temperature);
               ShowMsg(temperature);
            }
        }
    }

    // 发出语音警报
    private void MakeAlert(int param) {
       Console.WriteLine(“Alarm:嘀嘀嘀,水已经
{0} 度了:” ,
param);
    }
    
    // 突显水温
    private void ShowMsg(int param) {
       Console.WriteLine(“Display:水快开了,当前热度:{0}度。” , param);
    }
}

class Program {
    static void Main() {
       Heater ht = new Heater();
       ht.BoilWater();
    }
}
}

Observer设计格局简介

地点的例子明显能形成我们事先描述的行事,不过却并不够好。未来1旦磁能热水器由3局地构成:太阳能热水器、警报器、显示屏,它们出自于分歧厂商并拓展了组建。那么,应该是热水器只有负责烧水,它不可能发生警报也不能够显得水温;在水烧开时由警报器发出警报、显示器来得提示和水温。

那时候,下面的例证就活该成为这么些样子:   

// 热水器
public class Heater { 
    private int temperature;
        
    // 烧水
    private void BoilWater() {
       for (int i = 0; i <= 100; i++) {
           temperature = i;
        }
    }
}

// 警报器
public class Alarm{
    private void MakeAlert(int param) {
       Console.WriteLine(“Alarm:嘀嘀嘀,水已经
{0} 度了:” ,
param);
    }
}

// 显示器
public class Display{
    private void ShowMsg(int param) {
       Console.WriteLine(“Display:水已烧开,当前热度:{0}度。” , param);
    }
}

此处就出现了一个题材:怎样在水烧开的时候布告报告警察方器和屏幕?在后续打开事先,大家先驾驭一下Observer设计方式,Observer设计格局中最首要归纳如下两类对象:

  1. Subject:监视目的,它往往包蕴着其他对象所感兴趣的始末。在本范例中,电热水器便是二个蹲点目的,它涵盖的别样对象所感兴趣的剧情,正是temprature字段,当以此字段的值快到十0时,会四处把数量发给监视它的对象。
  2. Observer:监视者,它监视Subject,当Subject中的某件事产生的时候,会告知Observer,而Observer则会选用对应的行走。在本范例中,Observer有警报器和荧屏,它们利用的行路分别是发生警报和体现水温。

在本例中,事情发生的依次应该是这么的:

  1. 警报器和显示器告诉太阳能热水器,它对它的热度相比较感兴趣(注册)。
  2. 电热水器知道后保留对警报器和显示器的引用。
  3. 太阳能热水器实行烧水这一动作,当水温超过玖伍度时,通过对警报器和显示屏的引用,自动调用警报器的MakeAlert()方法、显示器的ShowMsg()方法。

看似那样的事例是很多的,GOF对它进行了说梅止渴,称为Observer设计方式:Observer设计形式是为了定义对象间的1种一对多的依靠关系,以便于当2个指标的意况改换时,其余注重于它的指标会被机关告知并革新。Observer格局是壹种松耦合的设计情势。

实现范例的Observer设计形式

大家在此以前曾经对信托和事件介绍很多了,以往写代码应该很轻巧了,未来在那里间接交给代码,并在诠释中加以印证。

using System;
using System.Collections.Generic;
using System.Text;

namespace Delegate {
    // 热水器
    public class Heater {
       private int temperature;
       public delegate void BoilHandler(int param);   //申明委托
       public event BoilHandler Boil伊芙nt;        //评释事件

       // 烧水
       public void BoilWater() {
           for (int i = 0; i <= 100; i++) {
              temperature = i;

              if (temperature > 95) {
                  if (BoilEvent != null) { //假使有目的注册
                      Boil伊芙nt(temperature);  //调用装有注册对象的法门
                  }
              }
           }
       }
    }

    // 警报器
    public class Alarm {
       public void MakeAlert(int param) {
           Console.WriteLine(“Alarm:嘀嘀嘀,水已经
{0} 度了:”, param);
       }
    }

    // 显示器
    public class Display {
       public static void ShowMsg(int param) { //静态方法
           Console.WriteLine(“Display:水快烧开了,当前热度:{0}度。”,
param);
       }
    }
    
    class Program {
       static void Main() {
           Heater heater = new Heater();
           Alarm alarm = new Alarm();

           heater.Boil伊芙nt += alarm.MakeAlert;    //注册格局
           heater.Boil伊芙nt += (new Alarm()).MakeAlert;   //给匿名对象注册情势
           heater.Boil伊芙nt += Display.ShowMsg;       //注册静态方法

           heater.Boil沃特er();   //烧水,会自动调用注册过对象的秘籍
       }
    }
}
输出为:
Alarm:嘀嘀嘀,水已经 96 度了:
Alarm:嘀嘀嘀,水已经 96 度了:
Display:水快烧开了,当前热度:玖6度。
// 省略…

.Net Framework中的委托与事件

尽管地方的范例很好地做到了笔者们想要完毕的行事,但是大家不光吸引:为何.Net
Framework 中的事件模型和方面包车型大巴两样?为啥有许多的伊夫ntArgs参数?

在答复上边的难点以前,大家先搞懂 .Net Framework的编码规范:

  • 寄托项目标名称都应该以伊夫ntHandler结束。
  • 委托的原型定义:有2个void再次回到值,并接受五个输入参数:四个Object
    类型,三个 伊夫ntArgs类型(或持续自伊夫ntArgs)。
  • 事件的命名称叫 委托去掉 伊夫ntHandler之后结余的部分。
  • 两次三番自伊夫ntArgs的种类应该以伊芙ntArgs结尾。

再做一下表达:

  1. 委托注明原型中的Object类型的参数代表了Subject,也正是监视指标,在本例中是
    Heater(空气能热水器)。回调函数(比如Alarm的MakeAlert)能够由此它访问触发事件的目的(Heater)。
  2. 伊夫ntArgs 对象涵盖了Observer所感兴趣的多寡,在本例中是temperature。

地方这几个其实不单是为着编码规范而已,那样也使得程序有更加大的灵活性。比如,假使大家不光想博得电热水器的热度,还想在Observer端(警报器或许显示器)方法中收获它的生产日期、型号、价格,那么委托和格局的宣示都会变得很麻烦,而假使大家将电热水器的引用传给警报器的办法,就能够在艺术中直接待上访问电热水器了。

近日大家改写此前的范例,让它符合 .Net Framework 的正规化:

using System;
using System.Collections.Generic;
using System.Text;

namespace Delegate {
    // 热水器
    public class Heater {
       private int temperature;
       public string type = “RealFire
00一”;       // 增多型号作为示范
       public string area = “China
Xian”;         // 增多产地作为示范
       //注解委托
       public delegate void BoiledEventHandler(Object sender,
BoiledEventArgs e);
       public event Boiled伊芙ntHandler Boiled; //注明事件

       //
定义Boiled伊芙ntArgs类,传递给Observer所感兴趣的新闻
       public class BoiledEventArgs : EventArgs {
           public readonly int temperature;
           public BoiledEventArgs(int temperature) {
              this.temperature
= temperature;
           }
       }

       // 能够供继承自
Heater 的类重写,以便继承类拒绝任何对象对它的监视
       protected virtual void OnBoiled(BoiledEventArgs e) {
           if (Boiled != null) { // 假诺有目的注册
              Boiled(this,
e);  //
调用全部注册对象的法子
           }
       }
       
       // 烧水。
       public void BoilWater() {
           for (int i = 0; i <= 100; i++) {
              temperature = i;
              if (temperature > 95) {
                  //建立BoiledEventArgs
对象。
                  BoiledEventArgs e = new BoiledEventArgs(temperature);
                  OnBoiled(e);  // 调用
OnBolied方法
              }
           }
       }
    }

    // 警报器
    public class Alarm {
       public void MakeAlert(Object sender,
Heater.BoiledEventArgs e) {
           Heater heater = (Heater)sender;     //这里是还是不是很熟习呢?
           //访问 sender
中的公共字段
           Console.WriteLine(“Alarm:{0} – {1}:
“, heater.area, heater.type);
           Console.WriteLine(“Alarm:
嘀嘀嘀,水已经 {0} 度了:”, e.temperature);
           Console.WriteLine();
C#,       }
    }

    // 显示器
    public class Display {
       public static void ShowMsg(Object sender,
Heater.Boiled伊夫ntArgs e) {   //静态方法
           Heater heater = (Heater)sender;
           Console.WriteLine(“Display:{0} – {1}:
“, heater.area, heater.type);
           Console.WriteLine(“Display:水快烧开了,当前热度:{0}度。”,
e.temperature);
           Console.WriteLine();
       }
    }

    class Program {
       static void Main() {
           Heater heater = new Heater();
           Alarm alarm = new Alarm();

           heater.Boiled += alarm.MakeAlert;   //注册方法
           heater.Boiled += (new Alarm()).MakeAlert;      //给匿名对象注册格局
           heater.Boiled += new Heater.Boiled伊夫ntHandler(alarm.MakeAlert);    //也能够那样注册
           heater.Boiled += Display.ShowMsg;       //注册静态方法

           heater.BoilWater();   //烧水,会自行调用注册过对象的法子
       }
    }
}

输出为:
Alarm:China Xian – RealFire 001:
Alarm: 嘀嘀嘀,水已经 96 度了:
Alarm:China Xian – RealFire 001:
Alarm: 嘀嘀嘀,水已经 96 度了:
Alarm:China Xian – RealFire 001:
Alarm: 嘀嘀嘀,水已经 96 度了:
Display:China Xian – RealFire 001:
Display:水快烧开了,当前热度:9陆度。
// 省略 …

总结

在本文中自小编第3通过三个GreetingPeople的小程序向大家介绍了信托的定义、委托用来做什么,随后又引出了事件,接着对信托与事件所发生的中间代码做了简约的描述。

在其次个稍微复杂点的太阳能热水器的范例中,笔者向大家轻巧介绍了
Observer设计情势,并因此实现这一个范例完毕了该情势,随后讲述了.Net
Framework中央委员托、事件的贯彻格局。

可望那篇小说能给您带来帮忙。

 

 

原稿转自:
http://www.cnblogs.com/jimmyzhang/archive/2007/09/23/903360.html

相关文章