去年学习C#的时候按照进度把C#的委托和事件“认真”的学习了一下,自己知道委托和事件的重要性,所以也努力的学习,可说实也没怎么学懂。碰巧这段时间在做一个解析GPS数据的小项目,因为其中有需要条件自动判断并处理的模块,因此首先想到的就是委托和事件。借此机会重新学习和复习了一下,收获颇丰。
什么是委托?根据我的理解委托就是把一件可能发生的事情,当满条件达到时委托别人帮忙处理。根据从书上看到的委托应该是有C++的函数指针发展而来的,只不过C#中一般不用指针。在这里举一个小例子,输入一个字符串当它的长度满足指定长度时输出,达到指定长度就是触发了事件。
class AutoDisposeStr
{
public AutoDisposeStr(int length)
{
LimitLength = length;
}
public delegate void DisplaysrtHandler();
public DisplaysrtHandler MeetLength;
public void OnReadAndDisplay()
{
Console.WriteLine(str);
}
public DisplaysrtHandler MeetLength
{
get { return _MeetLength; }
set { _MeetLength = value; }
}
public int LimitLength
{
get { return _LimitLength; }
set { _LimitLength = value; }
}
public string str
{
get { return _str; }
set
{
if (value.Length >= LimitLength)
{
_str = value;
MeetLength();
}
}
}
private int _LimitLength;
private string _str;
private DisplaysrtHandler _MeetLength;
}
}
首先构建了一AutoDisposeStr类有三个字段_LimitLength表示处理字符串的门限值对应的属性为LimitLength,_Str将要处理的字符串对应属性为Str,_MeetLength为委托类型的字段对应属性为MeetLength用来注册监听者。需要注意的是委托类型参数列表和你处理函数的参数列表需要对应,在这里就是 public delegate void DisplaysrtHandler()和public void OnReadAndDisplay()。因为要实时监听字符串长度是否达到门限值,所以要在字符串属性赋值时进行处理。每输入一个字符串,判断长度是否达到门限值,如果达到则调用监听函数处理:
public string str
{
get { return _str; }
set
{
if (value.Length >= LimitLength)
{
_str = value;
MeetLength();//调用监听函数处理
}
}
在这里其实就是把处理的方法委托给了public void OnReadAndDisplay()函数。最后只需要在Main()函数中实例化并注册监听函数即可。委托的用法就这么简单,不过有几点组要注意:
一:如果当前没有订阅者也就是MeetLength为空执行 MeetLength()会引发异常NullReferenceException,因此为了解决这个问题在触发事件之前检查空值。
DisplaysrtHandler local=MeetLength;
if(local!=null)
{
local();
}
这里定义了一个局部变量来检查是否为空是为了防止来自不同线程订阅者被移除可能造成NullReferenceException异常,因为MeetLength-=<listener>,会将一个全新的委托指派给MeetLength,这不会对原先local委托有任何影响。
二,委托链会按注册的顺序调用,但是如果一旦前面的委托发生异常,后面的委托将得不到执行。因此最保险的方法应该用foreach(DisplaysrtHandler myhander in local.GetInvocationList){}同时加上异常处理。
委托就说这么多吧,说的不对的地方欢迎指正共同学习。
接下来说说事件,我感觉事件就是在委托的基础上增加了一些编程的规范。对于委托你可以在任何包容类之外调用委托,这就会造成对包容类内委托结果的影响。不满足封装性要求,其次事件中当注册事件和移除事件时如果不幸将“-=”或“+=”写成了“=”在委托中没有语法错误而是造成了委托连被无意赋值号覆盖掉了,而在事件中这样的大意是不能编译通过的。防止了这种错误的可能性,还有事件中对事件处理函数参数有规范要求,这里就不多讲了。贴上事件的代码以做对比:
class AutoDisposeStr
{
public class AutoDisposeStrArgs : System.EventArgs
{ }
public AutoDisposeStr(int length)
{
LimitLength = length;
}
public delegate void DisplaysrtHandler(object sender, AutoDisposeStrArgs newAutoDisposeStr);
public event DisplaysrtHandler MeetLength;
public void OnReadAndDisplay(object sender, AutoDisposeStrArgs newAutoDisposeStr)
{
Console.WriteLine(str);
}
//public DisplaysrtHandler MeetLength
//{
// get { return _MeetLength; }
// set { _MeetLength = value; }
//}
public int LimitLength
{
get { return _LimitLength; }
set { _LimitLength = value; }
}
public string str
{
get { return _str; }
set
{
if (value.Length >= LimitLength)
{
_str = value;
MeetLength(this,null);
}
}
}
private int _LimitLength;
private string _str;
//private DisplaysrtHandler _MeetLength;
}
主函数:
class Program
{
static void Main(string[] args)
{
AutoDisposeStr ads= new AutoDisposeStr(5);
ads.MeetLength += ads.OnReadAndDisplay;
for (int i = 0; i < 10; i++)
{
ads.str = Console.ReadLine();
}
}
本人也是初学者,有说的不对不妥的地方欢迎指正,共同学习,共同进步。