.Net之美读书系列(二):委托进阶
这次看书的知识点:
- 事件访问器
- 如果一个委托中注册了多个事件且需要获取其返回值的方法
- 委托的异常处理
- 委托处理超时的方法
- 异步委托
事件访问器
职能有:
1.对委托属性进行封装,不再直接该委托变量直接进行-=和+=的操作,
2.保护委托变量只能支持注册一个事件,不能注册多个事件
3.从而即使委托中定义了返回值也不会被覆盖
还是直接上代码吧:
public static void Main(string[] args)
{
Publishser publish = new Publishser();
Subscriber subber = new Subscriber();
Subscriber subber2 = new Subscriber(); publish.NumberChanged += subber.OnNumberChange;
publish.NumberChanged += subber2.OnNumberChange;//会把上面的覆盖,最终只会注册最后的事件 publish.DoSomething();
Console.Read();
} } public delegate string GeneralEventHandler(); public class Publishser
{
//对委托属性进行封装,不再直接该委托变量直接进行-=和+=的操作,
//保护委托变量只能支持注册一个事件,不能注册多个事件
//从而即使委托中定义了返回值也不会被覆盖
private GeneralEventHandler numberChanger; //传说中的事件访问器,value属性是系统字段
public event GeneralEventHandler NumberChanged
{
add
{
numberChanger = value;
}
remove
{
//即使没有赋值也不会报错
numberChanger -= value;
}
} public void DoSomething()
{
if (numberChanger != null)
{
//调用带返回值的委托变量
string returnvalue = numberChanger();
Console.WriteLine("返回值:" + returnvalue);
}
}
} public class Subscriber
{
public string OnNumberChange()
{
string num = "";
Console.WriteLine("输入" + num);
return num;
}
}
获取委托中多个返回值
如果一个委托变量中注册了多个事件,那么需要逐个调用并且return对应的返回值,这时候可以使用List集合进行装载然后返回
public static void Main(string[] args)
{
Subscriber sub1 = new Subscriber();
Subscriber sbu2 = new Subscriber();
Subscriber sbu3 = new Subscriber();
Publisher pub = new Publisher();
//注册事件,注意:这里是直接对事件进行注册,并没有使用事件访问器
pub.numberChanged += sub1.sub;
pub.numberChanged += sbu2.sub;
pub.numberChanged += sbu3.sub; //触发事件
List<string> resultList= pub.DoSomething();
foreach (var item in resultList)
{
Console.WriteLine(item);
}
Console.Read();
} public delegate string DoneSomeThingEventHander(int num); /// <summary>
/// 发布者
/// </summary>
public class Publisher
{
//定义事件
public event DoneSomeThingEventHander numberChanged; public List<string> DoSomething(int parmeter)
{
List<string> resultList = new List<string>();
if (numberChanged == null)
{
return resultList;
} //按照调用顺序获取多路广播的列表(就是注册过的事件s)
Delegate[] delArray = numberChanged.GetInvocationList(); foreach (var item in delArray)
{
//因为委托其实编译成一个类,而且都是继承与Delegate,所以可以强制转换为对应的委托
DoneSomeThingEventHander menthod = (DoneSomeThingEventHander)item;
//读取订阅者的返回值
resultList.Add(menthod(parmeter));
}
return resultList;
}
} /// <summary>
///订阅者
/// </summary>
public class Subscriber
{
public string sub(int num)
{
Console.WriteLine(num);
return num.ToString();
}
}
如果直接调用事件,那么就是触发所有的注册的事件,返回值将会是最后一个事件的返回值,但是如果使用了事件的GetInvocationList()方法,那么其实就可以获取到曾经注册过的事件,然后根据注册的顺序逐一调用,就可以确定但返回值了.
委托的异常处理
如果一个委托变量,按照顺序注册了ABC个方法,调用完A后,B出错了,那么C就压根不会被执行,换句话说,3个订阅者,如果中间的订阅者报错了,那么后面的订阅者也没办法执行下去.如果直接在方法中try掉也是无法执行下去的.所以只能使用上面的GetInvocationList()获取到所有注册事件,然后逐个try一次,这样就不影响调用了.
public static void Main(string[] args)
{
Subscriber sub1 = new Subscriber();
Subscriber2 sbu2 = new Subscriber2();
Subscriber3 sbu3 = new Subscriber3();
Publisher pub = new Publisher();
//注册事件,注意:这里是直接对事件进行注册,并没有使用事件访问器
pub.numberChanged += sub1.sub;
pub.numberChanged += sbu2.sub;
pub.numberChanged += sbu3.sub; //触发事件
List<string> resultList= pub.DoSomething();
foreach (var item in resultList)
{
Console.WriteLine(item);
}
Console.Read();
} public delegate string DoneSomeThingEventHander(int num); /// <summary>
/// 发布者
/// </summary>
public class Publisher
{
//定义事件
public event DoneSomeThingEventHander numberChanged; public List<string> DoSomething(int parmeter)
{
List<string> resultList = new List<string>();
if (numberChanged == null)
{
return resultList;
} ////如果直接try掉,程序可以走下去,但是订阅者却不能订阅到事件
//try
//{
// resultList.Add( numberChanged(parmeter));
//}
//catch (Exception ex)
//{
// return new List<string>();
//} //第二种异常的处理方式
Delegate[] delArray = numberChanged.GetInvocationList();
foreach (var item in delArray)
{
//这样try掉的话,就不影响其他的订阅者了
try
{
DoneSomeThingEventHander menthod = (DoneSomeThingEventHander)item;
resultList.Add(menthod(parmeter));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return resultList;
}
} /// <summary>
///订阅者
/// </summary>
public class Subscriber
{
public string sub(int num)
{
Console.WriteLine(num);
return num.ToString();
}
} /// <summary>
///订阅者2
/// </summary>
public class Subscriber2
{
public string sub(int num)
{
throw new Exception("我是订阅者2的错误");
return string.Empty;
}
} /// <summary>
///订阅者2
/// </summary>
public class Subscriber3
{
public string sub(int num)
{
Console.WriteLine("没报错");
return "没报错";
}
}
}
订阅者方法超时的处理方式(异步调用)
订阅者除了可以通过异常的方式来影响发布者以外,还可以通过另一种方式:超时。超时和异常的区别就是超时并不会影响事件的正确触发和程序的正常运行,却会导致事件触发后需要很长才能够结束。能做。因为当执行订阅者方法时(通过委托,相当于依次调用所有注册了的方法),当前线程会转去执行方法中的代码,调用方法的客户端会被中断,只有当方法执行完毕并返回时,控制权才会回到客户端,从而继续执行客户端接下来的代码.
委托的定义会生成继承自MulticastDelegate的完整的类,其中包含Invoke()、BeginInvoke()和EndInvoke()方法。Invoke()直接调用委托阻断客户端,BeginInvoke()和EndInvoke()则是一个配套的方法,BeginInvoke()执行完之后,客户端就不用再管,会启动一个闲置的线程执行订阅者的方法,
public static void Main(string[] args)
{
Subscriber sub1 = new Subscriber();
Subscriber2 sbu2 = new Subscriber2();
Subscriber3 sbu3 = new Subscriber3();
Publisher pub = new Publisher();
//注册事件,注意:这里是直接对事件进行注册,并没有使用事件访问器
pub.numberChanged += sub1.sub;
pub.numberChanged += sbu2.sub;
pub.numberChanged += sbu3.sub; //触发事件
List<string> resultList = pub.DoSomething();
foreach (var item in resultList)
{
Console.WriteLine("最后输出结果的");
}
Console.WriteLine("客户端的最后");
Console.Read();
} public delegate string DoneSomeThingEventHander(int num); /// <summary>
/// 发布者
/// </summary>
public class Publisher
{
public event DoneSomeThingEventHander numberChanged;
public List<string> DoSomething(int parmeter)
{
List<string> resultList = new List<string>();
if (numberChanged == null)
{
return resultList;
}
//如果注册的事件有多个,那么只能逐个调用BeginInvoke(),否则会报错
Delegate[] delArray = numberChanged.GetInvocationList();
foreach (var item in delArray)
{
try
{
DoneSomeThingEventHander menthod = (DoneSomeThingEventHander)item;
//参数为null的,书中是迟点再讨论,
menthod.BeginInvoke(parmeter, null, null);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
return resultList;
}
} /// <summary>
///订阅者
/// </summary>
public class Subscriber
{
public string sub(int num)
{
Thread.Sleep();
Console.WriteLine("睡了1秒");
return "睡了1秒";
}
} /// <summary>
///订阅者2
/// </summary>
public class Subscriber2
{
public string sub(int num)
{
Thread.Sleep();
Console.WriteLine("睡了2秒");
return "2秒";
}
} /// <summary>
///订阅者2
/// </summary>
public class Subscriber3
{
public string sub(int num)
{
Console.WriteLine("没睡");
return "没睡";
}
}
}
以上只是一个最简单的最简单的不阻断主线程的方法,其实完成的方法应该是配合EndInvoke()一起使用的.调用BeginInvoke()就是相当于开了一个后台线程去运行方法,只要主线程不关掉,那么后台线程也会继续执行知道它执行完成为止.
异步委托
1.使用异步委托,解决等待问题,并且如何获取委托中处理完结果的方法
class Program
{ static void Main(string[] args)
{
Console.WriteLine("客户端开启"); Calculator c = new Calculator();
//委托变量
AddEventHandler addmenthod = new AddEventHandler(c.Add); //addmenthod += c.Add;//如果要异步调用,调用的委托变量只能注册一个方法,否则会报错 //在调用委托时,begininvoke会动态组成参数,前面的参数就是方法的参数了
IAsyncResult asyncResult = addmenthod.BeginInvoke(, , null, null);//不使用委托调用完后回调函数,
for (int i = ; i <= ; i++)
{
Thread.Sleep(i * );
Console.WriteLine("{0}: Client executed {1} second( s).", Thread.CurrentThread.Name, i); } //如果想获取委托的返回值,则使用EndInvoke,如果是在同一个方法中当然可以使用,原来委托的对象,如果不是在同一个方法中,则可以通过 IAsyncResult asyncResult 这个返回值来获得委托对象
//int delegateresult = addmenthod.EndInvoke(asyncResult); //在别的方法中处理获得异步委托处理的结果(和上面的是一样的,只是在第二个地方获取返回的结果而已)
int delegateresult = c.getRusult(asyncResult); Console.WriteLine("委托执行结果"+delegateresult); Console.WriteLine(Thread.CurrentThread.Name + "程序执行完成");
Console.Read();
}
} //定义个对应的需要异步调用的方法的委托
public delegate int AddEventHandler(int x, int y); public class Calculator
{
public int Add(int x, int y)
{
if (Thread.CurrentThread.IsThreadPoolThread)
{
Thread.CurrentThread.Name = "Pool Thread"; }
Console.WriteLine("方法线程开始执行");
for (int i = ; i <= ; i++)
{
Thread.Sleep(i * );
Console.WriteLine(Thread.CurrentThread.Name + "执行了" + i + "秒");
}
Console.WriteLine("方法执行完了");
return x + y;
} /// <summary>
/// 定义一个方法获取返回的结果
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
public int getRusult(IAsyncResult asyncresult)
{
//强制转换为AsyncResult对象(多态的原因)
AsyncResult result = (AsyncResult)asyncresult;
//获取原来委托的对象并强制转换
AddEventHandler del = (AddEventHandler)result.AsyncDelegate;
int returnresult = del.EndInvoke(asyncresult);
return returnresult;
}
}
如果要在别的地方获取委托的结果,只需要传入委托begininvoke返回的 IAsyncResult asyncResult对象,参照 public int getRusult(IAsyncResult asyncresult)即可.
2.如果在委托没有执行完,但是又调用了委托的EndInvoke()方法的话,就会阻塞主线程,那么我们不知道什么时候才会执行调用完,这时候在调用beginInvoke的倒数第二个参数就是传入一个如果方法结束了的委托的对象了.
class Program
{ static void Main(string[] args)
{
Console.WriteLine("客户端开启"); Calculator c = new Calculator();
//委托变量
AddEventHandler addmenthod = new AddEventHandler(c.Add); int parmeter = ;
AsyncCallback callback = new AsyncCallback(c.getRusult);
//这次传入倒数第二个参数,就是一个回调函数,,这个回调函数就是当委托执行完之后回调的方法,倒数第一个参数可以在委托中拿取到
//callback是一个以IAsyncResult为参数的,返回值为void的方法的委托
IAsyncResult asyncResult = addmenthod.BeginInvoke(, , callback, parmeter);
for (int i = ; i <= ; i++)
{
Thread.Sleep(i * );
Console.WriteLine("{0}: Client executed {1} second( s).", Thread.CurrentThread.Name, i); } Console.WriteLine(Thread.CurrentThread.Name + "程序执行完成");
Console.Read();
}
} //定义个对应的需要异步调用的方法的委托
public delegate int AddEventHandler(int x, int y); public class Calculator
{
public int Add(int x, int y)
{
if (Thread.CurrentThread.IsThreadPoolThread)
{
Thread.CurrentThread.Name = "Pool Thread"; }
Console.WriteLine("方法线程开始执行");
for (int i = ; i <= ; i++)
{
Thread.Sleep(i * );
Console.WriteLine(Thread.CurrentThread.Name + "执行了" + i + "秒");
}
Console.WriteLine("方法执行完了");
return x + y;
} /// <summary>
/// 定义一个方法获取返回的结果
/// </summary>
/// <param name="result"></param>
/// <returns></returns>
public void getRusult(IAsyncResult asyncresult)
{
//强制转换为AsyncResult对象(多态的原因)
AsyncResult result = (AsyncResult)asyncresult;
//获取原来委托的对象并强制转换
AddEventHandler del = (AddEventHandler)result.AsyncDelegate;
int returnresult = del.EndInvoke(asyncresult);
Console.WriteLine("委托执行完了,输出传入的参数"+result.AsyncState.ToString());
}
}
这次就到这里,最大的收获应该是对委托的理解吧
.Net之美读书系列(二):委托进阶的更多相关文章
- .Net之美读书系列(一):委托与事件
开启新的读书之旅,这次读的书为<.Net之美:.Net关键技术深入解析>. 我是选择性阅读的,把一些自己觉得容易忘记的,或者比较重要的知识点记录下来,以便以后能方便呢查阅. 尊重书本原作者 ...
- Wireshark入门与进阶系列(二)
摘自http://blog.csdn.net/howeverpf/article/details/40743705 Wireshark入门与进阶系列(二) “君子生非异也,善假于物也”---荀子 本文 ...
- Bing Maps进阶系列二:使用GeocodeService进行地理位置检索
Bing Maps进阶系列二:使用GeocodeService进行地理位置检索 在<Bing Maps进阶系列一:初识Bing Maps地图服务>里已经对GeocodeService的功能 ...
- Spring Boot进阶系列二
上一篇文章,主要分析了怎么建立一个Restful web service,系列二主要创建一个H5静态页面使用ajax请求数据,功能主要有添加一本书,请求所有书并且按照Id降序排列,以及查看,删除一本书 ...
- Web 前端开发人员和设计师必读精华文章【系列二十六】
<Web 前端开发精华文章推荐>2014年第5期(总第26期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...
- Web 前端开发精华文章推荐(HTML5、CSS3、jQuery)【系列二十二】
<Web 前端开发精华文章推荐>2014年第一期(总第二十二期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML ...
- Web 前端开发精华文章集锦(jQuery、HTML5、CSS3)【系列二十】
<Web 前端开发精华文章推荐>2013年第八期(总第二十期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各种增强网站用户体验的 jQuery 插件,展示前沿的 HTML5 和 C ...
- [知识库分享系列] 二、.NET(ASP.NET)
最近时间又有了新的想法,当我用新的眼光在整理一些很老的知识库时,发现很多东西都已经过时,或者是很基础很零碎的知识点.如果分享出去大家不看倒好,更担心的是会误人子弟,但为了保证此系列的完整,还是选择分享 ...
- Newtonsoft.Json C# Json序列化和反序列化工具的使用、类型方法大全 C# 算法题系列(二) 各位相加、整数反转、回文数、罗马数字转整数 C# 算法题系列(一) 两数之和、无重复字符的最长子串 DateTime Tips c#发送邮件,可发送多个附件 MVC图片上传详解
Newtonsoft.Json C# Json序列化和反序列化工具的使用.类型方法大全 Newtonsoft.Json Newtonsoft.Json 是.Net平台操作Json的工具,他的介绍就 ...
随机推荐
- Learning WCF Chapter2 Data Contracts
A data contract describes how CLR types map to XSD schema definitions. Data contracts are the prefer ...
- 老的acm & oj学习站点
1.网易小鱼博客 http://gisyhy.blog.163.com/blog/#m=0&t=1&c=fks_087069086082087064085081082095085084 ...
- 【转】Linux(ubuntu14.04)上编译Android4.4源码的环境搭建及编译全过程
原文网址:http://jileniao.net/linux-android-building.html sublime text让我伤心.本来很信任sublime text的自动保存功能,之前使用一 ...
- Devexpress 之gridControl双击行事件
MouseDown事件 protected internal void gridControl1_MouseDown(object sender, MouseEventArgs e) { DevExp ...
- MonkeyRunner执行Python脚本实例——发送短信增强版
很久之前就写好的了,准备写个自动执行Monkey的脚本时才想到去找它,还是写在博客里找起来方便. 这次更新了批处理自动连接设备后执行Py脚本,结构如下图: 其中shotscreen为存放截图文件夹,s ...
- java 枚举(括号赋值)
详解在这里 要注意的是: 1. 通过括号赋值,而且必须带有一个参构造器和一个属性跟方法,否则编译出错2. 赋值必须都赋值或都不赋值,不能一部分赋值一部分不赋值:如果不赋值则不能写构造器,赋值编译也出错 ...
- HDOJ/HDU 1241 Oil Deposits(经典DFS)
Problem Description The GeoSurvComp geologic survey company is responsible for detecting underground ...
- 连续使用两次fread 错误和fread返回值
今天在写一个代码,要把一帧的buffer读入到文件,因为有NEON和OpenCL两种不同的实现所以需要读取文件两次,代码如下: FILE *file; ; INTER_BLOCK_SIZE_GPU_R ...
- S2SH邮件注册激活后注册成功
首先我的思路是这样的:①接收从客户端接收过来的数据(密码,用户名,邮箱) ②将密码进行MD5加密,然后将信息用"_"连接起来(用于后面分解) ③将信息交个一个工具类中实现生成邮件信 ...
- kafka consumer 分区reblance算法
转载请注明原创地址 http://www.cnblogs.com/dongxiao-yang/p/6238029.html 最近需要详细研究下kafka reblance过程中分区计算的算法细节,网上 ...