C# 代理实际上类似于C++中的函数指针,因为C#中不存在指针,所以用代理可以完成一些原来在C++中用函数指针完成的操作,例如传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。
实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate:
1.声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。
声明一个代理的例子:
public delegate int MyDelegate(string message);
2.创建delegate对象,并将你想要传递的函数作为参数传入。
创建代理对象的方法:
1). MyDelegate myDelegate = new MyDelegate(实例名.方法名);
2). MyDelegate myDelegate = new MyDelegate(类名.方法名);
注:如果需要代理的方法是一个static静态方法的话,采用第2种方式,否则采用第1种方式。
3.在要实现异步调用的地方,通过上一步创建的对象来调用方法。
可以直接使用代理调用代理所指向的方法:
myDelegate(向方法传递的参数);
下面是一些需要注意的事情:
“代理”(delegate)(代表、委托):“代理”是类型安全的并且完全面向对象的。
(1)在C#中,所有的代理都是从System.Delegate类派生的(delegate是System.Delegate的别名)。
(2)代理隐含具有sealed属性,即不能用来派生新的类型。
(3)代理最大的作用就是为类的事件绑定事件处理程序。
(4)在通过代理调用函数前,必须先检查代理是否为空(null),若非空,才能调用函数。
(5)在代理实例中可以封装静态的方法也可以封装实例方法。
(6)在创建代理实例时,需要传递将要映射的方法或其他代理实例以指明代理将要封装的函数原型(.NET中称为方法签名:signature)。注意,如果映射的是静态方法,传递的参数应该是类名.方法名,如果映射的是实例方法,传递的参数应该是实例名.方法名。
(7)只有当两个代理实例所映射的方法以及该方法所属的对象都相同时,才认为它们是想等的(从函数地址考虑)。
(8)多个代理实例可以形成一个代理链,System.Delegate中定义了用来维护代理链的静态方法Combion,Remove,分别向代理链中添加代理实例和删除代理实例。
(9)代理三步曲:
a.生成自定义代理类:delegate int MyDelegate();
b.然后实例化代理类:MyDelegate d = new MyDelegate(MyClass.MyMethod);
c.最后通过实例对象调用方法:int ret = d();
C#中的delegate和C++中的函数指针基本是一回事,C#正是以delegate的形式实现了函数指针。不同的地方在于C#中delegate是类型安全的。
如果没有C或者C++的背景知识也没有一点关系。
要理解Delegate,首先,你要明白,它是一个类,他和Class是一个级别的概念,不同在于Class的定义包含字段和方法,而delegate只包含方法的细节。
定义Delegate:
public delegate bool CompareDelegate(int a, int b);
上面语句的含义是:定义了一个委托的实例时,这些实例都要包含一个方法名,并且这个方法必须有两个int型的参数,并且必须返回一个bool型的值。
好,现在就创建一个上面定义的委托的实例
public CompareDelegate myCompare = new CompareDelegate(CompareMethod);
里面的这个CompareMethod是一个方法:
定义如下:
public static bool CompareMethod(int x , int y)
{
bool result = x > y ? true : false;
return result;
}
怎么使用呢
int a = 10;
int b = 15;
bool isBigger = myDelegate(a, b);
下面是完整的代码:
using System;
using System.Collections.Generic;
using System.Text;
using Common;
namespace ConsoleApplication8
{
//定义委托
public delegate bool CompareDelegate(int a, int b);
public class MyCompare
{
/// <summary>
/// 定义一个方法,符合上面定义的委托所要求的方法细节,返回值为bool型,两个参数分别为int型
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public static bool CompareMethod(int x , int y)
{
bool result = x > y ? true : false;
return result;
}
}
class Program
{
static void Main(string[] args)
{
//实例化委托并初始化。注意初始化时候,必须包含一个方法的名称,而且这个方法必须符合最初定义委托时候的签名(参数类型和返回值)
CompareDelegate myDelegate = new CompareDelegate(MyCompare.CompareMethod);
//现在我们使用委托实例来比较两个数a和b的的大小
int a = 10;
int b = 15;
bool isBigger = myDelegate(a, b);
Console.WriteLine(" a > b ?: " + isBigger);
Console.Read();
}
}
}
1. 什么是托管函数
托管函数是一个对类里面的某个函数的一个引用.它自己并没有具体的函数定义,只是指向某个函数实现.
2. 与C++, Delphi的横向比较
在C++和Delphi中与托管函数对应的类型是函数指针,形式如下:
C++
typedef (int GetNum)(real rNum);
Delphi
function GetNum(rNum:real): integer; external;
这里定义函数指针,他们所能指向的函数定义的传入参数必须是实型,传出为整形
3. 托管函数有什么用
由于托管函数是对类里面某个函数的一个引用.所以我们不必知道这个函数的具体名字是什么,而只需要调用托管函数,让托管函数去调用相应的函数就可以了. 一个例子: 一个公司,一个领导,一个领导秘书,三个员工分别管理市场,策划和生产.这里秘书就相当与一个托管函数, 领导要下达什么命令(获得市场信息,生产什么产品),只需要对秘书说给我一份市场报告或者我们不生产原子弹转向生产氢弹.然后再由秘书根据领导的命令选择的去找哪个员工,再把员工获得的信息返回给领导. 这里面三个员工对于领导是透明的,领导并不知道自己下达的命令具体是由谁执行的.
4. 如何定义&&如何使用
接着上面的例子,我们声明一个秘书托管函数原型
class Secretory
{
//定义托管函数原型
private delegate string Do(string command);
//创建托管函数实例
public Do Doit;
//给秘书下达命令执行
public void ExecuteCommand(string command)
{
switch(秘书根据命令判断是要给谁执行的)
{
case 调研市场的人:
Doit=new Do(new MarketMan().GetMarketInfo);
Break;
Case 生长产品的人:
Doit=new Do(new ProductMan().ProduceProduct);
Break;
Default:
Break;
}
}
Doit(command);
}
///调研市场的那个人的类
class MarketMan
{
public string GetMarketInfo(string command)
{ 根据命令调查信息, 返回获得信息 ;}
}
///生产产品的人
class ProductMan
{
public static string ProduceProduct(string command)
{ 根据命令生产产品,并返回生产工程中的信息;}
}
ok,那么如何执行呢?老板怎么使用Secretory这个类呢?
go on
老板只需要把秘书叫(new)过来,直接向他下达命令,然后就可以喝茶去了~ ^_^
class Boss
{
public void static main()
{
(new Secretory()).Doit(“我们要生产氢弹,把以前的原子弹都扔到日本好了”);
}
}
5. 其他
正入你们所见的,上面的两个类中的方法,一个是静态,一个不是静态的. 托管函数所指的函数可以静态的也可以不是静态的.
delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,delegate类能够拥有一个签名(signature),并且它只能持有与它的签名相匹配的方法的引用。它所实现的功能与C/C++中的函数指针十分相似。它允许你传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate:
1.声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。
2. 创建delegate对象,并将你想要传递的函数作为参数传入。
3. 在要实现异步调用的地方,通过上一步创建的对象来调用方法。using System;
public class MyDelegateTest
{
// 步骤1,声明delegate对象
public delegate void MyDelegate(string name);
// 这是我们欲传递的方法,它与MyDelegate具有相同的参数和返回值类型
public static void MyDelegateFunc(string name)
{
Console.WriteLine("Hello, ", name);
}
public static void Main()
{
// 步骤2,创建delegate对象
MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);
// 步骤3,调用delegate
md("sam1111");
}
}
输出结果是:Hello, sam1111
了解了delegate,下面我们来看看,在C#中对事件是如何处理的。
C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子:
public delegate void MyEventHandler(object sender, MyEventArgs e);
其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArgs类用来包含与事件相关的数据,所有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含参数,那么可以直接用System.EventArgs类作为参数。
就是这么简单,结合delegate的实现,我们可以将自定义事件的实现归结为以下几步:
1.定义delegate对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。
2.定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。
3.定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。
4. 用event关键字定义事件对象,它同时也是一个delegate对象。
5.用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。
6.在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是OnEventName。
7. 在适当的地方调用事件触发方法触发事件。
下面是一个简单的例子:
using System;
public class EventTest
{
// 步骤1,定义delegate对象
public delegate void MyEventHandler(object sender, System.EventArgs e);
// 步骤2省略
public class MyEventCls
{
// 步骤3,定义事件处理方法,它与delegate对象具有相同的参数和返回值类// 型
public void MyEventFunc(object sender, System.EventArgs e)
{
Console.WriteLine("My event is ok!");
}
}
// 步骤4,用event关键字定义事件对象
private event MyEventHandler myevent;
private MyEventCls myecls;
public EventTest()
{
myecls = new MyEventCls();
// 步骤5,用+=操作符将事件添加到队列中
this.myevent += new MyEventHandler(myecls.MyEventFunc);
}
// 步骤6,以调用delegate的方式写事件触发函数
protected void OnMyEvent(System.EventArgs e)
{
if(myevent != null)
myevent(this, e);
}
public void RaiseEvent()
{
EventArgs e = new EventArgs();
// 步骤7,触发事件
OnMyEvent(e);
}
public static void Main()
{
EventTest et = new EventTest();
Console.Write("Please input ’’a’’:");
string s = Console.ReadLine();
if(s == "a")
{
et.RaiseEvent();
}
else
{
Console.WriteLine("Error");
}
}
}
学习c#都会惊奇于它的delegate,其实delegate并没有太多神秘的地方,说的通俗点,delegate是类型化了的函数指针,它主要应用于回调。 学习c++的对函数指针都不回陌生,它是一个保存了函数地址的变量,但除了地址,它没有包含任何额外的信息,如参数的个数、参数类型和函数的返回地址等,所以函数指针是非类型安全的。而delegate对回调提供了类型的安全性,从而使我们在处理回调等问题的时候可以更加优美的面向对象的方式来编写代码,并且在CLR的内部对delegate的操作提供了许多支持(如delegate链表),简化了我们常用的操作。 在使用delegate的过程中,首先要定义一个delegate的类型,如: public delegate void SomeFunction(Object a,int i,...); note:它是一个类的定义,可以放在任何地方,此类型定义了此delegate所接收的函数的样式:返回void,参数列表... 然后,为了使用,要定义类SomeFunction的一个实例: public SomeFunction instance; 接着,我们就可以往这个delegate实例中放“符合规范”的函数(可以为实例函数和静态函数)。 instance +=new SomeFunction(someObject.SomeMethod); 其中someObject为某类的一个实例,它的方面SomeMethod符合此委托的规范要求,否则将编译报错。 最后,可以直接调用instance实例来实现对someObject的SomeMethod的回调。 instance(...); ########################## 只是从上面的实现中,貌似看不出delegate真正存在的价值,但实际上delegate增加了对链的支持,我们可以象下面这样应用: instance +=new SomeFunction(someObject.SomeMethod); instance +=new SomeFunction(anotherObject.anotherMethod); ... 然后调用instance(...),可以同时激发所有注册自里面的回调函数。 这是怎样实现的,首先要研究delegate内在的结构,在每个delegate里面包括了三个字段: target ——》指向回调函数所属的对象实例(对于实例方法来言) method ——》指向回调函数 prev ——》指向另外一个delegate 实例 通过prev就可以比较方便的实现delegate对链的支持。 CLR定义了Delegate.Combine和Delegate.Remove静态方法实现对链表的操作。