也叫作委托。事实上,代理就是用于定义指向方法的引用。
比如你在你眼前的程序中要调用另一部分程序的内容(方法或属性),但是,你不能保证函数名或者属性名不发生变化,或者根本程序不可见(不是public或者是DLL的程序)。那么就使用代理。
定义如下
public delegate UInt32[] getDownLoadParam();
public delegate void messgeFromDataLayer(Int32 info, Int32 info1, Int32 info2);
getDownLoadParam m_getDownLoadParam;
messgeFromDataLayer m_messgeFromDataLayer;
在模块初始化时为他们赋值。
m_getDownLoadParam = GetDownLoadParam;
m_messgeFromDataLayer = MessgeFromDataLayer;
函数名可以直接转化为代理类型的对象。这样,你就可以向普通函数一样调用他们了。
m_messgeFromDataLayer(1, 0, 0);
一个完整的使用如下
using System;
namespace sandbox
{
class DelegateTest
{
public delegate void CompareDelegate(int a, int b);
// 欲传递的方法,它与CompareDelegate具有相同的参数和返回值类型
public static void Compare(int a, int b)
{
Console.WriteLine((a > b).ToString());
}
public static void Main()
{
// 创建delegate对象
CompareDelegate cd = new CompareDelegate(DelegateTest.Compare);
// 调用delegate
cd(1, 2);
}
}
除了代理外,C#还有一个常见的语法糖。event关键字。上面代理可以让程序动态调用函数指针处理。但是,如果我们希望的处理函数不止一个,特别是对于界面上。所以,我们引入了event关键字。可以向中间添加若干个代理的实例,将依次调用。
event的声明是event+事件支持的代理名+event名。直接用event名就可以出发事件。一个event可以添加若干个事件处理的代理实例,但他们必须都是一个代理类型的。一个代理类型可以由多个代理的实例。
using System;
namespace sandbox
{
class DelegateTest
{
public static void Main()
{
//创建事件源,并将事件处理程序进行注册
EventSource src = new EventSource();
src.OnClick += new EventSource.ClickHandler(MyFire1);
src.OnClick += new EventSource.ClickHandler(MyFire2);
src.Click();
System.Console.ReadLine();
}
public static void MyFire1(object sender, ButtonClickArgs e)
{
Console.WriteLine(e.Clicker+" hava been shot in 1");
}
public static void MyFire2(object sender, ButtonClickArgs e)
{
Console.WriteLine(e.Clicker+" hava been shot in 2");
}
}
class EventSource
{
public delegate void ClickHandler(object sender, ButtonClickArgs e);
public event ClickHandler OnClick;
public void Click()
{
OnClick(this, new ButtonClickArgs() { Clicker = "Signal" });
}
}
public class ButtonClickArgs : EventArgs
{
public string Clicker;
}
}
这种delegate和event共同使用的情景常用语界面的设计
如果你不使用代理,而执意要用public,那么在访问form的子类的对象的值类型成员时,编译器会给出CS1690警告:由于xxx是引用封送类的字段,访问上面的成员可能导致运行时异常。
这是因为form类是MarshalByRefObject的子类,该类旨在设计支持不同程序域的对象进行通信。一般来说,不同程序域的通信有两种方式,第一种就是直接传送对象的副本,另一种是使用代理。MarshalByRefObject使用的的是后一种。当有人要远程访问他的对象是,第一次他会向对方传递代理,然后如果对方调用该代理,就由己方的对象负责执行。
所以,如果你访问form类的对象A所包含的对象B的方法时,就将程序置于依赖远程对象执行的代码的风险之下,所以编译器会发送警告。解决的方法就是把对象B先复制到本地就可以了。
using System;
class WarningCS1690: MarshalByRefObject
{
int i = 5;
public static void Main()
{
WarningCS1690 e = new WarningCS1690();
e.i.ToString(); // CS1690
// OK
int i = e.i;//c#是system.int32的别名而已
i.ToString();
e.i = i;
}
}