要区分三者之间的联系与区别首先要明白它们的定义
什么是委托?
委托是类型安全的类,它定义了返回类型和参数类型。委托由关键字delegate来声明
什么情况使用委托?
当要把方法传送给其他方法是,需要使用委托。
既然委托是将方法作为参数传递它,那么委托与指针有什么联系?
委托是面向对象,是类型安全的。
在C++中,函数指针只不过是一个指向内存位置的指针,它不是类型安全的。
委托包含:普通委托,lambda表达式和匿名方法,事件
一、普通委托(单路委托,多播委托,泛型委托(Action<T>和Func<T>))
定义一个委托类的语法:
[访问限制符] delegate [返回值类型] [委托类的名称]( [参数列表] );
1.单路委托(
单路委托就是和方法建立一对一的关系)
2.多播委托(多播委托就是和方法建立一对多的关系)
3.泛型委托
泛型Action<T>委托类表示引用一个void返回类型的方法
Func<T>委托的使用方式和Action<T>委托类似.Func<T>允许调用带有返回值的方法
二、匿名方法和lambda表达式
1.匿名方法(是用作委托的参数的一段代码)
用匿名方法定义委托的语法与前面的定义没有区别,但在实例化委托时有所区别,如下实例说明了如何使用匿名方法
static void Main()
{
string mid=",middle part,";
Func<string,string> anonDel=delegate(string param)
{
param +=mid;
param +="and this was added to the string.";
return param;
};//结尾的";"别忘记
Console.WriteLine(anonDel("Start of string"));
}
/*使用匿名委托的时,要遵守两个原则:
1、匿名方法中不能有跳转语句(break, goto或continue)跳转到匿名方法的外部,反之,外部代码也不能跳转到该匿名方法内部。
2、在匿名方法中不能访问不安全代码。
注意:不能访问在匿名方法外部使用的ref和out参数。*/
2.Lambda表达式(只要有委托参数类型的地方,就可以使用Lambda表达式)
语法:
[委托类] [委托对象名] = ( [参数列表] ) => { /*代码块*/ }; //结尾还是有一个分号
将前面使用匿名方法的例子改为lambda表达式:
static void Main()
{
string mid=",middle part,";
Func<string,string> lambda=param=>
{
param +=mid;
param +="and this was added to the string.";
return param;
};//结尾的";"别忘记
Console.WriteLine(lambda("Start of string"));
}
三、事件
事件基于委托,为委托提供了一种发布/订阅机制。事件是一种特殊多播委托,换句话来说,事件是经过深度封装的委托。一个事件简单的可以看作一个多播委托加上两个方法(+=订阅消息和-=取消订阅)。
1、普通事件
我们使用event关键字来声明事件,语法如下:
[访问权限修饰符] event [委托类类名] [名称];
事件一般是用于通知代码发生了什么。由此我们又可以引出两个概念:1、事件发布方2、事件侦听方。我们现在用一个简单的例子来说明这两个概念,我们以烧开水为例,当水温为95至100度时发出警报。我们先来定义在事件发生时,需要传输的数据成员:
public class Water //事件发布程序中的基本数据成员类
{
public int Temperature { get; private set; }
public Water(int t)
{
this.Temperature = t;
}
}
有了传输的数据,那么我们现在就可以定义事件触发类::
public delegate void WaterHandler(object sender, Water w); //sender为事件发送者,w为发送的数据
public class Heater //事件发布程序中的,事件触发类
{
public event WaterHandler WaterEvent; //深度的封装委托
public void HeatWater() //该方法用于触发事件
{
for (int i = 0; i < 101; i++)
{
if (i > 95 && i < 101)
{
RegWaterEvent(i); //触发事件
}
}
}
protected virtual void RegWaterEvent(int t)
{
WaterHandler temp = WaterEvent;
if (temp != null)
temp(this, new Water(t)); //如果委托不为空,我们就执行委托,我们无需知道具体执行了哪些方法
}
}
现在我们已经完整了定义好了事件发布方了,通过这个例子我们也知道了事件发布方由两部分组成:1、基本数据类 2、事件触发类。接下来我们继续看看事件侦听方又是怎么样的:
public class Alarm //事件侦听类
{
public void Waring(object sender, Water w) //侦听接口,由于侦听事件的发布
{
Console.WriteLine("当前水温已经到达 {0} ℃!", w.Temperature);
}
}
通过这个例子我们可以发现,事件侦听类,只需有一个和被监听事件一致的方法即可。
Heater heater = new Heater(); //生成事件发布实例
Alarm alarm = new Alarm();
heater.WaterEvent += alarm.Waring; //对事件发布方进行订阅(侦听),反之我们使用-=取消订阅
heater.HeatWater(); //触发事件,那么现在Alarm类对象alarm将会侦听到这次事件
通过上述例子我们就大致的了解了事件的工作情况,以及事件发布方和事件侦听方的概念。
.Net平台为我们提供了泛型委托EventHandler<T>,有了这个泛型委托之后我们就不在需要定义委托类了。我们来看看泛型委托EventHandler<T>的原型:
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e)
where TEventArgs: EventArgs;
参数列表中第一个参数是对象,包含事件的发送者,第二个参数提供了事件的相关信息。现在我们定义事件时,只需让基本数据类继承EventArgs,然后我们就能泛型委托来定义事件了。
注意 :事件只能在本类型内部“触发”,委托不管在本类型内部还是外部都可以“调用”。事件在类的外部只能使用+=或-=来增加/取消订阅