一、委托
1 定义
抛开书上的意义,委托其实就是方法的代理。
事例:比如你需要让3个人,每个人分别去做某一件事情,但此时你有这三个人的代理人(即这三个人的主管或者上司),只需要做的事情就是告诉这个代码人这三个人分别要做的事情,之后这个代理人便会自动地分别告诉这三个人应该去做什么事情。
委托是一种类型,它可以定义在类的内部,也可以定义在类的外部,其关键字为:delegate.
2 使用
关于委托的使用,只需要记住以下的步骤即可:
① 声明一个委托;
② 定义一个委托变量;
③ 为该委托变量指定其需要“代理”的方法;
④ 执行该委托
事例:比如现在你有三台打印机(生活中一个人大概不会同时使用三台,此处只是做为事例说明)A,B,C,每个打印机只有一个方法,即打印,如下:
-
C# code
-
class PrintA { public void PrintAPaint() { Console.WriteLine( " A号打印机在打印 " ); } } class PrintB { public void PrintBPaint() { Console.WriteLine( " B号打印机在打印 " ); } } class PrintC { public void PrintCPaint() { Console.WriteLine( " C号打印机在打印 " ); } }
现在你不想一台一台地操纵打印机,你想通过一个委托来帮你进行操纵,那么这时,你就要创建一个委托,我们在一个控制器中(Controller类)进行委托的相关操作,首先,你得声明一个委托,如下:
-
C# code
-
// 第一步:声明委托(注:委托其实是一种与"类"平行的概念,它可以在类的内部声明,也可以在类的外部声明,如同类一样,在使用类之前你得先创建一个类) public delegate void PrintPaintdelegate();
其次,你得定义一个委托变量(注:此处可以想到类,一个类在你使用的时候,首先得new一个出来,此处我们就是“new”一个委托)
-
C# code
-
// 第二步:定义委托变量 public PrintPaintdelegate pPaintDelegate;
接下来,你得为你的委托定义添加删除方法(注:委托其实是一种代理,要想代理,需要为其提供要代理的方法)
-
C# code
-
// 第三步:将想要通过委托调用的方法传递给该委托变量,可以传递给委托多个方法 // 添加方法 public void Add(PrintPaintdelegate ppd) { pPaintDelegate += ppd; } // 移除某个方法 public void Remove(PrintPaintdelegate ppd) { pPaintDelegate -= ppd; }
关于第三步,为委托添加删除方法,该方法必须与该委托的参数个数,参数类型,参数类型及返回值一样,否则编译不能通过,另外,此处的Add与Remove方法并非必须,也可以直接通过 pPaintDelegate += ppd;或pPaintDelegate -= ppd;添加删除方法,这个随大家的意愿。
委托及相关的方法此时已经全部定义了出来,该是Main方法出场的时候了,直接上代码:
-
C# code
-
// 初始化相关的变量 PrintA pA = new PrintA(); PrintB pB = new PrintB(); PrintC pC = new PrintC(); // 定义控件器 Controller col = new Controller(); // 为控制器添加方法 col.Add(pA.PrintAPaint); col.Add(pB.PrintBPaint); col.Add(pC.PrintCPaint); // 执行委托 col.pPaintDelegate(); Console.ReadLine();
在运行该程序后,输出结果如下:
A号打印机在打印
B号打印机在打印
C号打印机在打印
关于执行委托的一点说明:执行委托与执行方法的操作是一致的,有参则传参,无参则不需要参数。
最后再说一点,委托的出现就是为了事件而服务的,它的主要应用目前我知道的就是事件,所以你当你再问“委托主要是干什么用的问题”的时候,就默默地在心理回答:“它的出现就是为了事件而服务的”,:-),以上是对于委托的理解。
二、事件
事件是一种特殊的委托,关于事件的使用,与委托的使用没有太多的区别,其步骤如下:
① 声明一个委托;
② 定义一个事件变量;
③ 为该委托变量指定其需要“代理”的方法;
④ 执行该事件
事例,我们还使用刚刚定义的打印机的事件,关于打印机的具体打印方法在此处不再多说,只说一个控制器类,直接上代码:
-
C# code
-
// 委托相关的操作 // 第一步:声明委托 public delegate void PrintPaintdelegateEvent(); // 第二步:定义事件变量 public event PrintPaintdelegateEvent pPaintDelegateEvent; // 第三步:将想要通过委托调用的方法传递给该委托变量,可以传递给委托多个方法 // 添加方法 public void Add(PrintPaintdelegateEvent ppd) { pPaintDelegateEvent += ppd; } // 移除某个方法 public void Remove(PrintPaintdelegateEvent ppd) { pPaintDelegateEvent -= ppd; } // 执行方法 public void Run() { this .pPaintDelegateEvent(); }
注意,此处中的第二步里,在定义的委托变量时,增加了关键字,even,此为委托与事件的区别之一,再看我们的Main方法:
-
C# code
-
// 初始化相关的变量 PrintA pA = new PrintA(); PrintB pB = new PrintB(); PrintC pC = new PrintC(); // 定义控制器 Controller col = new Controller(); // 为控制器添加方法 col.Add(pA.PrintAPaint); col.Add(pB.PrintBPaint); col.Add(pC.PrintCPaint); // 删除某个委托中的方法 col.Remove(pB.PrintBPaint); // 事件不能如委托一样在外部调用,即不能如下调用,即便该方法是Public的 // col.pPaintDelegate(); // 只能在定义该事件的类的内部调用,代码如下: col.Run(); Console.ReadLine();
在该Main方法中,与上一个委托事例中不同的地方在于,执行事件的时候,不能通过col.pPaintDelegate();执行,而只能在定义该事件的类的内部调用该事件(委托),这个是事件与委托的区别之二。
可以这样理解,事件就是一种只能在定义该事件的类的内部执行的一种特殊的委托。
但是此处就有另外的一个问题了,为什么事件与委托基本上一致,还要区分事件与委托呢?其实这个问题我一开始也不太清楚,后来经过查阅资料才明白,原因是这样的:
一个委托变量,如果是pulbic的,比如上边的pPaintDelegate,可以在控制器类的内部调用,也可以在类的内部调用,但不知道大家想过没想过一个问题,如果某个人不小在该控件器类内部,初始化该类的时候,直接为该委托变量直接增加了该控制器内定义的一个私有方法,然后在main方法中执行,那么此时,该类内部的私有方法,还如何私有呢?而事件的出现,正在为了弥补这缺点的,我想大家应该明白是怎么回事了。
以上是我对这二者的理解,请各位看一下哪里有不对的地方,以便于进一步的改正,谢谢!
委托声明定义一种类型,它用一组特定的参数以及返回类型封装方法。对于静态方法,委托对象封装要调用的方法。对于实例方法,委托对象同时封装一个实例和该实例上的一个方法。如果您有一个委托对象和一组适当的参数,则可以用这些参数调用该委托。
注意 委托是在调用方的安全权限下运行而不是声明方的权限下运行。
实例化委托 声明了委托类型后,必须创建委托对象并使之与特定方法关联。与所有其他对象类似,新的委托对象用 new 表达式创建。但是当创建委托时,传递给 new 表达式的参数很特殊:它的编写类似于方法调用,但没有方法的参数。
调用委托 创建委托对象后,通常将委托对象传递给将调用该委托的其他代码。通过委托对象的名称(后面跟着要传递给委托的参数,括在括号内)调用委托对象。
抛开定义用法,最主要我们要理解为啥要是委托,什么时候要用委托,给我们带来了那些方便
委托是给其他调用者使用的,它的最大特点是调用者不知道什么时候该调用它,所以只好提前订阅它然后做自己的事不去管它...当委托对象有了订阅者感兴趣的消息就会通知调用者...这是方法不能很容易做到的...
想象一下订阅报纸或牛奶的过程 ,看看Windows消息机制和事件驱动机制将有助于你理解委托...
事件
C# 中的“事件”是当对象发生某些有趣的事情时,类向该类的客户提供通知的一种方法。事件最常见的用途是用于图形用户界面;通常,表示界面中的控件的类具有一些事件,当用户对控件进行某些操作(如单击某个按钮)时,将通知这些事件。
但是事件未必只用于图形界面。事件为对象提供一种通常很有用的方法来发出信号表示状态更改,这些状态更改可能对该对象的客户很有用。事件是创建类的重要构造块,这些类可在大量的不同程序中重复使用。
使用委托来声明事件。如果您尚未学习“委托教程”,您应先学习它,然后再继续。请回忆委托对象封装一个方法,以便可以匿名调用该方法。事件是类允许客户为其提供方法(事件发生时应调用这些方法)的委托的一种方法。事件发生时,将调用其客户提供给它的委托。
声明事件 若要在类内声明事件,首先必须声明该事件的委托类型(如果尚未声明的话)。
public delegate void ChangedEventHandler(object sender, EventArgs e);
委托类型定义传递给处理该事件的方法的一组参数。多个事件可共享相同的委托类型,因此仅当尚未声明任何合适的委托类型时才需要执行该步骤。
接下来,声明事件本身。
public event ChangedEventHandler Changed;
声明事件的方法与声明委托类型的字段类似,只是关键字 event 在事件声明前面,在修饰符后面。事件通常被声明为公共事件,但允许任意可访问修饰符。
调用事件 类声明了事件以后,可以就像处理所指示的委托类型的字段那样处理该事件。如果没有任何客户将委托与该事件挂钩,该字段将为空;否则该字段引用应在调用该事件时调用的委托。因此,调用事件时通常先检查是否为空,然后再调用事件。
if (Changed != null)
Changed(this, e);
调用事件只能从声明该事件的类内进行。
与事件挂钩 从声明事件的类外看,事件像个字段,但对该字段的访问是非常受限制的。只可进行如下操作:
在该字段上撰写新的委托。
从字段(可能是复合字段)移除委托。
使用 += 和 -= 运算符完成此操作。为开始接收事件调用,客户代码先创建事件类型的委托,该委托引用应从事件调用的方法。然后它使用 += 将该委托写到事件可能连接到的其他任何委托上。
// Add "ListChanged" to the Changed event on "List":
List.Changed += new ChangedEventHandler(ListChanged);
当客户代码完成接收事件调用后,它将使用运算符 -= 从事件移除其委托。
// Detach the event and delete the list:
List.Changed -= new ChangedEventHandler(ListChanged);
事件和继承
当创建可以从中派生的通用组件时,事件中有时出现似乎会成为问题的情况。由于事件只能从声明它们的类中调用,因此派生类不能直接调用在基类内声明的事件。虽然这有时符合需要,但通常使派生类能够自由调用事件更合适。这通常通过为事件创建受保护的调用方法来实现。通过调用该调用方法,派生类便可以调用此事件。为获得更大的灵活性,调用方法通常声明为虚拟的,这允许派生类重写调用方法。这使得派生类可以截获基类正在调用的事件,有可能对这些事件执行它自己的处理。
在前面的示例中,这已用 OnChanged 方法实现。如果需要,派生类可调用或重写该方法。
接口中的事件
事件和字段之间的另一个差异是,事件可放在接口中,而字段不能。当实现接口时,实现类必须在实现接口的类中提供相应的事件。