文章类型: .NET
关键词: 委托
内容摘要:

委托

2015/7/29 11:56:26    来源:apple    阅读:

委托是C#中新加入的一个类型,可以把它想作一个和Class类似的一种类型,和使用类相似,使用一个委托时,需要两个步骤,首先你要定义一个委托,就像是定义一个类一样;然后,你可以创建一个或多个该委托的实例。

定义一个委托的语法是这样的:

[public/protected/private] delegate returnType delegateName(paramtype param1,…)

这是我自己写的,看起来好像有点怪怪的,我来解释一下,private/protected/private是限定词,就不多说了,delegate是申明一个委托的关键词,returnType是一个返回类型,delegateName是一个你给委托起的名字,你可以写任何一个你喜欢的名字,paramtype param1…这个是参数列表。说了这么多可能看起来还是不是很好理解,我是这样认为的,实用点来讲,委托定义就是在一个函数定义中间加入一个delegate的关键词。它的作用类似于你申明一个类:

public class ClassName {…}

创建一个委托的实例:

[public/protected/private] delegateName deleInstanceName = new delegateName(MethodName)

这个类似于实例化一个类,public ClassName instancename = new ClassName(…),这里有个要注意的地方,即MethodName方法要和delegateName的签名一致。什么是签名一致,就是说MethodName的参数列表,返回值要分别和returnType(paramtype param1,…)一致。举个例子来说明下:

public delegate string DelegateDemo(string name, int age);

比如我们如上定义了一个委托,就写法来说,其实就是在函数 string DelegateDemo(string name, int age)前面加了一个delegate的关键字,下面我们来用创建一个函数:

public string AgentDemo(string name, int age)

{

string rev = “”;

     return rev;

}

这个函数是做参数传递给一个DelegateDemo实例的,接下来创建一个DelegateDemo的实例:

DelegateName instanceDemo = new DelegateName(AgentDemo);

这时要说到一致了,即AgentDemo和声明委托时的DelegateDemo(我们姑且将delegate去掉)这两个函数的返回值,参数列表要相同。终于说完了,不知道看的人明不明白。

接下来,我们可以使用这个委托了(调用一个委托),如下:

string name = “cshape”;

int age = 20;

instanceDemo(name, age);

instanceDemo执行时,会执行AgentDemo函数,instanceDemo相当于C里的一个函数指针,现在这个指针指向AgentDemo的函数入口地址。

2.多点委托

前面提到的委托都只包含对一个方法的调用,如果需要调用多个方法,需要多次显示的调用这个委托,我们还有另的选择,我们可以让一个委托中包含多个方法,这样我们一次显示调用委托,就可以按照顺序连续的调用多个方法。看下面的例子:

public delegate void MultiDelegate(string name);

public void AgentDemo1(string str)

{

     Console.WriteLine(str + “this is AgentDemo1\n”);

}

 

public void AgentDemo2(string s)

{

     Console.WriteLine(s + “this is AgentDemo2\n”);

}

 

MultiDelegate multiDemo = new MultiDelegate(AgentDemo1);

multiDemo += new MultiDelegate(AgentDemo2);

multiDemo(“multiDemo test :”);

 

输出的结果应该是:

multiDemo test :this is AgentDemo1

mutliDemo test :this is AgentDemo2

 

可以看到我们一次显示的调用一个委托,它顺序的(按照你添加方法的顺序)执行了方法AgentDemo1AgentDemo2。这里要注意的有几点:

●     委托支持 +=-=这样的运算符,对应为添加或去掉一个方法

●     多点委托不可以定义有返回值,因为无法处理多个方法的返回值,所以如果要使用多点委托,应该用void,否则你的编译会返回一个错误

●     多点委托不建议你的参数列表中有OUT的类型,这样只会out最后一个方法的值,其他的值会丢失。

3.委托的理解

首先申明,这只是我举的一个例子,目的是帮助理解委托的过程,其中很多地方都经不起推敲,望大家知悉。言归正传,

你想要吃饭,

但是自己又不会做(委托方不知道实现细节),

你计划找个饭店,叫个回锅肉饭(定义了一个委托)

你决定找常去的那家叫做A的饭店(实例化一个委托)

你打电话给A饭店(委托调用)

A饭店给你做好了你的回锅肉饭(代理函数工作)

饭来了,真好。

 

4.委托的使用时机

当你需要把一个方法传送给其他方法时,可以考虑使用委托。好像不是很好理解,也可以这样说,当你确定要处理一件事,但又不能确定处理方法时,可以考虑用委托。其实单独的说委托的应用好像有点牵强,委托更多的是在事件中的应用。

5.一个委托的例子

我用两个类来做这个例子,一个类,我称它为委托方,一个类我称它为代理方,代码如下:

using System;

 

namespace Visen.Demo.Delegate

{

     ///<summary>

     /// StartUp 委托演示中的程序入口,含委托方。

     ///</summary>

     class StartUp

     {

     #region公用的方法

 

         #region应用程序的主入口点。

         ///<summary>

         ///应用程序的主入口点。

         ///</summary>

         [STAThread]

         static void Main(string[] args)

         {

              Console.WriteLine("This is a delegate demo\n");

 

              Visen.Demo.Delegate.Agent ag = new Agent();

 

              //定义一个委托类型的对象

              OutMessage singleDele = new OutMessage(ag.ShowMessage);

              OutMessage deleStaticMeth = new OutMessage(Agent.SShowMessage);

 

              //定义一个多点委托

              OutMessage MultiDele = new OutMessage(ag.ShowMessage);

              MultiDele += new OutMessage(Agent.SShowMessage);

           

              singleDele(" delegate instance singleDele");

              deleStaticMeth(" delegate instance deleStaticMeth");

              MultiDele(" this is a MultiDele");

              Console.Read();

         }

         #endregion应用程序的主入口点。

 

     #endregion公用的方法

 

     #region私用的字段

         ///<summary>

         ///定义一个委托类型

         ///</summary>

         private delegate void OutMessage(string msg);

     #endregion私有的字段

     }

}

 

下面是代理方:

using System;

 

namespace Visen.Demo.Delegate

{

     ///<summary>

     /// Agent 类为委托者的代理方,处理委托者委托的事务。

     ///</summary>

     public class Agent

     {

     #region公用的方法

 

         #region空的构造函数

         ///<summary>

         ///空的构造函数

         ///</summary>

         public Agent()

         {

         }

         #endregion空的构造函数

 

         #region显示一条信息到控制台,一个类成员函数作为代理方

         ///<summary>

         ///显示一条信息到控制台,一个类成员函数作为代理方

         ///</summary>

         ///<param name="msg">显示内容</param>

         public void ShowMessage(string msg)

         {

              Console.WriteLine("Method ShowMessage out:" + msg + "\n");

         }

         #endregion显示一条信息到控制台,一个类成员函数作为代理方

 

         #region显示一条信息到控制台,一个类静态函数作为代理方

         ///<summary>

         ///显示一条信息到控制台,一个类静态函数作为代理方

         ///</summary>

         ///<param name="msg">显示信息</param>

         public static void SShowMessage(string msg)

         {

              Console.WriteLine("static Method SShowMessage out:" + msg + "\n");

         }

         #endregion显示一条信息到控制台,一个类静态函数作为代理方

 

     #endregion公用的方法

     }

}

输出为:

This is a delegate demo

 

Method ShowMessage out: delegate instance singleDele

 

static Method SShowMessage out: delegate instance deleStaticMeth

 

Method ShowMessage out: this is a MultiDele

 

static Method SShowMessage out: this is a MultiDele

 

可见:方法函数可以是类成员函数,也可以是一个静态成员,只要和委托的签名相同就可以了。

 

有错的地方,请大家批评指正,谢谢!

 

 

如果你学过C/C++,最简单的解释就是委托就是高级的函数指针

用自然语言解释的话,委托就是端菜的。你去餐馆吃饭点菜都是点服务员的,看上去服务员什么都会做,实际都是服务员【委托】给厨师做把菜给你的。

也就是说委托有两种能力:

1.作为一个方法的符号,表示任何签名相同的方法,这个和C/C++是一样的。

2.异步能力。委托可以通过BeginInvoke异步执行,这是.NET特有的。

另外事件也是一种委托

 

相信你委托的文章看过不少,所以写法就不用多说了,我想困扰你的应该是为什么要用委托?什么时候用委托?

委托有个很重要的功能,比如:A类可以调用B类的方法,这是很常用的吧,但是A类怎么调用B类的事件呢?这就要用到委托了,你可以理解为函数指针。

 

委托很复杂 我简单点的说你就明白了。

委托有什么用?我们在用vs.net开发程序时 在开发界面放一个按钮 点击它 在后台就会生成对这个按钮的click事件 然后我能就可以在该事件的代码里面写要处理的事。该click事件事怎么和按钮联系起来的呢?用国asp.net的都知道 this.BtnReturn.Click += new System.Web.UI.ImageClickEventHandler(this.BtnReturn_Click);

这里就是个委托 最后面的this.BtnReturn_Click就是按钮的cilick事件的函数 这个函数我们可以随便改 例如this.BtnReturn.Click += new System.Web.UI.ImageClickEventHandler(do);只要我们存在有个do()的函数就可以了,这就是委托的作用。给一个行为添加函数,至于函数的内容可以让用户自己来写。接口不太好说,自己慢慢领会。多态范围太大 举个简单的例子 就是构造函数。它允许你定义几个名字一样但参数不一样的函数

 

委托(C# 编程指南)
委托是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值,如下面的示例所示:
C#
public delegate int PerformCalculation(int x, int y);
与委托的签名(由返回类型和参数组成)匹配的任何方法都可以分配给该委托。这样就可以通过编程方式来更改方法调用,还可以向现有类中插入新代码。只要知道委托的签名,便可以分配自己的委托方法。
将方法作为参数进行引用的能力使委托成为定义回调方法的理想选择。例如,可以向排序算法传递对比较两个对象的方法的引用。分离比较代码使得可以采用更通用的方式编写算法。
 
委托概述
委托具有以下特点:
 
委托类似于 C++ 函数指针,但它是类型安全的。
委托允许将方法作为参数进行传递。
委托可用于定义回调方法。
委托可以链接在一起;例如,可以对一个事件调用多个方法。
方法不需要与委托签名精确匹配。有关更多信息,请参见协变和逆变。
C# 2.0 版引入了匿名方法的概念,此类方法允许将代码块作为参数传递,以代替单独定义的方法。
如何使用委托,请看
http://msdn.microsoft.com/zh-cn/library/ms173172(VS.80).aspx
事件(C# 编程指南)
在发生其他类或对象关注的事情时,类或对象可通过事件通知它们。发送(或引发)事件的类称为“发行者”,接收(或处理)事件的类称为“订户”。
在典型的 C# Windows 窗体或 Web 应用程序中,可订阅由控件(如按钮和列表框)引发的事件。可使用 Visual C# 集成开发环境 (IDE) 来浏览控件发布的事件,选择要处理的事件。IDE 会自动添加空事件处理程序方法和订阅事件的代码。有关更多信息,请参见如何:订阅和取消订阅事件(C# 编程指南)。
事件概述
事件具有以下特点:
发行者确定何时引发事件,订户确定执行何种操作来响应该事件。
一个事件可以有多个订户。一个订户可处理来自多个发行者的多个事件。
没有订户的事件永远不会被调用。
事件通常用于通知用户操作(如:图形用户界面中的按钮单击或菜单选择操作)。
如果一个事件有多个订户,当引发该事件时,会同步调用多个事件处理程序。要异步调用事件,请参见使用异步方式调用同步方法。
可以利用事件同步线程。
在 .NET Framework 类库中,事件是基于 EventHandler 委托和 EventArgs 基类的。
关于如何使用事件相关的知识,请看
http://msdn.microsoft.com/zh-cn/library/ms366768(VS.80).aspx
这个可以看出他和委托的联系


 

 

c#事件与委托理解???

引用而来:

事件与委托似乎很难以理解,这是因为它们的使用方式与常用的编码有很大的差别,例如通常编写的都是同步代码,调用一个类型的方法,会即刻出现方法执行的结果,这是符合逻辑的。但在某些情况中,同步代码未必满足需求,拿公共汽车来打个比方,如果交通管制中心希望每一辆公车到达一个站点时都发送给自己一个信号以便自己能够随时掌握交通状况,使用同步代码,公汽对象肯定需要调用管制中心对象,这样就出现了我们一直不愿意看到的情况:两个类型紧密地耦合在一起。既然要其它类型对自己的行为作出反应,亲自调用其类型的方法似乎不可避免,在同步代码中,很难避免这种紧密的类型调用关系。

另一个差别是在一般情况下,我们只将属性作为参数传递给方法,而很少会考虑将一个方法传递给另一个方法。

我们抛弃各种C#参考书中桀骜难懂的事件与委托概念,设想一个情景来理解事件与委托的使用:有一家IT公司,董事长不希望自己的雇员在上班时间玩游戏,但又不可能每时每刻都盯着每个雇员,因此,他希望使用一种新的方式实现监视雇员的效果:如果有雇员违反规定,某个设备或专门的监查人员将自动发出一个消息通知他,董事长只需要在事情发生时进行处理。

因此,这个用例实际上是两种类型——董事长类与雇员类——之间的交互,下面的代码将给读者展示如何使用委托与事件机制实现这种交互:

 

首先,我们需要在董事长类与雇员类之间定义一个委托类型,用于传递两者之间的事件,这个类型就是一个监视设备或专门负责打小报告的监查人员:

 

public delegate void DelegateClassHandle();

 

定义一个委托的过程类似方法的定义,但它没有方法体。定义委托一定要添加关键字delegate。由于定义委托实际上相当一个类,因此可以在定义类的任何地方定义委托。另外,根据委托的可见性,也可以添加一般的访问修饰符,如publicprivateprotected

 

委托的返回值类型为void,这并非表示委托类型本身带有返回值,该返回值类型是指委托的目标函数类型,即它委托的一个事件处理函数返回值是void类型。

 

新建一个雇员类Employee,其代码如下:

public class Employee

{

    public event DelegateClassHandle PlayGame;

    public void Games()

    {

        if (PlayGame != null)

        {

            PlayGame();

        }

    }

}

雇员类Employee代码中定义了一个DelegateClassHandle类型的事件PlayGame,它的定义方式也很特殊,首先必须使用关键字event,表示PlayGame是一个事件,同时还必须声明该事件的委托类型为DelegateClassHandle,即将来由该类型的委托对象负责通知事件。

 

如果有雇员开始玩游戏,它将执行Games方法,而只要该方法一被调用,就会触发一个事件PlayGame,然后董事长就会收到这个事件的消息——有人在玩游戏了。

董事长类代码如下,他有一个方法Notify用于接收消息:

public class Admin

{

    public void Notify()

   {

        System.Console.WriteLine("someone is playing game");

    }

}

 

EmployeePlayGame事件如何与AdminNotify方法关联起来呢?只需通过事件绑定即可实现,具体过程如下列代码:

Employee employee = new Employee();

Admin admin = new Admin();

employee.PlayGame += new DelegateClassHandle(admin.Notify);

employee.Games();

 

请大家注意事件绑定的代码:

employee.PlayGame += new DelegateClassHandle(admin.Notify);

 

通过DelegateClassHandle将两个类的交互进行了绑定,当employee.Games方法调用后,触发PlayGame事件,而该事件将被委托给adminNotify方法处理,通知董事长有雇员在上班时间玩游戏。

 

但董事长并不满足这种简单的通知,他还想知道究竟是谁在上班时间违反规定。显然,现在委托对象必须传递必要的参数才行,这个要求也可以很容易地办到。事件的参数可以设置为任何类型的数据,在.NET框架中,还提供了事件参数基类EventArgs专门用于传递事件数据。

从该EventArgs类派生一个自定义的事件参数类CustomeEventArgs,这个类型将携带雇员姓名和年龄信息:

 

public class CustomeEvetnArgs : EventArgs

{

    string name = "";

    int age = 0;

    public CustomeEvetnArgs()

    { }

    public string Name

    {

        get { return this.name; }

        set { this.name = value; }

    }

    public int Age

    {

        get { return this.age; }

        set { this.age = value; }

    }

}

修改委托类型DelegateClassHandle的定义,让其携带必要的参数:

public delegate void DelegateClassHandle(object sender, CustomeEvetnArgs e);

雇员类的代码修改后如下:

public class Employee

{

    private string _name;

    public string Name

    {

        get { return _name; }

        set { _name = value; }

    }

    private int _age;

    public int Age

    {

        get { return _age; }

        set { _age = value; }

    }

    public event DelegateClassHandle PlayGame;

    public void Games()

    {

        if (PlayGame != null)

        {

            CustomeEvetnArgs e = new CustomeEvetnArgs();

            e.Name = this._name ;

            e.Age = this._age;

            PlayGame(this, e);

        }

    }

}

Games方法中,首先新建一个CustomeEventArgs对象,然后设置了必要的属性NameAge

董事长的通知方法也必须相应地进行修改:

public class Admin

{

    public void Notify(object sender, CustomeEvetnArgs e)

    {

        System.Console.WriteLine(e.Name+" is "+e.Age.ToString());

    }

}

 

将两个类型对象进行关联的代码也需要进行相应的修改:

Employee employee = new Employee();

employee.Name = "Mike";

employee.Age = 25;

Admin admin = new Admin();

employee.PlayGame += new DelegateClassHandle(admin.Notify);

employee.Games();

修改后的代码运行的结果是,当Mike调用Games方法玩游戏时,会自动触发PlayGame事件,而该事件携带相关信息通知admin,后者的Notify方法将接收到数据并输出“Mike is 25”,告诉董事长是Mike25岁,正在上班时间玩游戏。

委托是可以多路广播(Mulitcast)的,即一个事件可以委托给多个对象接收并处理。在上面的用例中,如果有另一位经理与董事长具有同样的癖好,也可以让委托对象将雇员的PlayGame事件通知他。

首先定义经理类:

public class Manager

{

    public void Notify(object sender, CustomeEvetnArgs e)

    {

        System.Console.WriteLine(sender.ToString() + "-" + e.Name);

    }

}

经理Manager类型的Notify方法与Admin一致,他也接受到相应的信息。

委托的多路广播绑定的方法仍然是使用+=运算符,其方法如下面的代码所示:

Employee employee = new Employee();

employee.Name = "Mike";

employee.Age = 25;

Admin admin = new Admin();

Manager manager = new Manager();

employee.PlayGame += new DelegateClassHandle(admin.Notify);

employee.PlayGame += new DelegateClassHandle(manager.Notify);

employee.Games();

执行该方法,读者将看到adminmanagerNotify方法都会被事件通知并调用执行。通过这样的方法,董事长和经理都会知道Mike在玩游戏了。

 

如果董事长不希望经理也收到这个通知,该如何解除PlayGamemanager的事件绑定呢?同样非常简单,在employee.Games方法被调用前执行下列语句即可:

employee.PlayGame -= new DelegateClassHandle(manager.Notify);

 

最后需要提醒读者注意的,Employee类中的Games方法在触发事件PlayGame之前需要判断该事件是否为null。当employee对象的Games方法触发事件PlayGame后,必须有一个目标函数来处理这个事件,而该语句正是判断该目标函数是否存在。如果将这个判断去掉,且对事件不进行任何绑定而直接调用Games方法,程序将在事件PlayGame处弹出一个NullReferenceException的异常。

 

读者能够从委托与事件的代码中得出什么结论吗?两个需要存在调用关系的类型,在各自的实现中却没有编写实际的调用代码,它们只是通过一个事件和一个第三方的委托类型完成了消息的传递过程。两个类型之间不存在任何的紧密耦合,它们看似松散地通过一个委托对象中通信,实现了本书一直宣传的高聚合低耦合观点。

↑ 上一篇文章:ImageMap控件 关键词:ImageMap,控件 发布日期:2015/7/29 11:54:38
↓ 下一篇文章:类型“System.Web.UI.UpdatePanel”不具有名为xxx的公共属性 解决方法 关键词:类型,System,Web,UI,UpdatePanel.. 发布日期:2015/7/29 11:57:51
相关文章:
如何理解.net下的委托和事件 关键词:.net,理解,委托,事件 发布日期:2015-07-28 12:14
C# 委托知识总结及示例 关键词:C#,委托,知识,总结,示例,实例 发布日期:2015-07-29 12:42
相关目录:.NET软件开发JAVAANDROID
我要评论
正在加载评论信息......