1.多播委托:由与delegate关键字声明的委托,在编译后默认继承Delegate与MulticastDelegate类型,所以声明的委托自然就含有多播委托的特性,即一个委托变量可以调用一个方法链(多个相同签名的方法)。在C#中,多播委托的实现是一个通用的模式,目的是避免大量的手工编码,这个模式称为Observer(观察者)或者publish-subscribe(发布-订阅)它要应对的就这样一种情形,你需要将单一事件的通知(比如对象状态发生了一个变化)广播给多个订阅者。

2.将“-=”运算符应用于委托会返回一个新实例:既然委托是一个引用类型,那么肯定会有人觉得奇怪,为什么赋值给一个局部变量,再用那个局部变量就可以保证Null检查的线程安全性?由于localOnChange指向的位置就是OnTemperatureChange指向的位置,所以很自然的一个结论就是:OnTemperatureChange中发生的任何改变都会在localOnChange中反映出来。但实情真是这样的吗?

  答案是否定的,因为事实上对OnTemperatureChange -= <listener> 的任何调用都不会从OnTemperatureChange删除一个委托而使它的委托比之前少一个。相反,会将一个全新的多播委托指派给它,这不会对原始的多播委托产生任何影响(localOnChange也指向那个原始的多播委托)。可查看Thermostat类中的代码。

3.委托运算符:“+=”与“-=”的操作,代表添加与移除一个委托方法,使用“=”赋值运算符会清除所有的订阅者,当然使用“+”与“-”操作效果是一样的。当然无论“+= -= + -”这些操作符,在内部都是使用静态方法Delegate.Combine()和Delegate.Remove()来实现的。Combine()方法支持两个参数都为null。

4.委托的顺序调用:在一个委托变量中含有多个委托方法(订阅者),其执行一次委托变量(发布),不是同时执行期内含有的多个委托方法,而是顺序执行含有的委托方法,通常是按照添加时的顺序执行委托链中的方法,但这个顺序有可能被覆盖,所以程序员不应依赖于一个特定的调用顺序。

5.多播委托的内部机制:前面已经说明delegate关键字的声明委托,会继承Delegate与MulticastDelegate类型,也就是说delegate关键字是派生自MulticastDelegate的一个类型的别名。MulticastDelegate类除了包含一个对象引用(Target)和方法指针(Method),这和它的Delegate基类是一样的,除此之外还包含对另一个MulticastDelegate对象的引用。向一个多播委托添加一个方法时,MulticastDelegate类会创建一个委托类型的新实例,在新实例中为新增的方法存储对象引用与方法指针,并在委托实例列表中添加新的委托实例作为下一项,这样的结果就是,MulticastDelegate类维护着由多个Delegate对象构成的一个链表(可以理解为多播委托内部有一个类似集合功能来管理多个委托方法)。

6.多播委托的错误处理:错误处理凸显了顺序通知的重要性,假如一个订阅者引发了一个异常,链中的后续订阅者就接收不到通知。为了避免这个问题,使所有的订阅者都收到通知,必须手动遍历订阅者列表,并单独调用它们,把它们放到try-catch块中。可查看Thermostat类中的代码。

7.方法返回值与参数引用:在多播委托中若要获得每一个订阅者的返回值,需要手工遍历委托链接收返回值,否则直接调用委托链会返回最后一个委托方法的返回值,同理ref、out修饰的参数也是一样的。

8.事件与委托的区别:在Thermostat类中,可以不使用event关键字修饰委托变量,直接使用委托变量处理也能完成相应的工作,但是直接使用委托,存在2个隐患,分别为“使用赋值运算符覆盖了以前的委托方法,可以在声明类的外部执行发布”,使用了event就可以对委托进行封装(由编译器生成封装的代码)。相应的2个隐患就消除了,其只能在声明类中执行发布,只能对委托进行注册与消除(不能覆盖,清空)。

9.EventHandler<TEventArgs>委托是.net2.0添加的泛型委托,内中包含object类型触发源,事件标记参数(可继承扩展其他数据),在任何类中使用sender-EventArgs模式,都不必声明它自己的委托定义,可以直接使用EventHandle泛型委托。

10.事件的内部机制:C#编译器会获取带有event修饰符的public委托变量,并将委托声明private,除此之外,它还添加了2个方法和2个特殊的事件块。从本质上说,event关键字是编译器用于生成恰当封装逻辑的一个C#快捷方式。可查看Thermostat2类的代码。

11.自定义事件的实现:编译器为“+=”与“-=”生成的代码是可以自定义的,例如,假定改变OnTemperatureChange委托的作用域,使它成为protected而不是private,这样就可以从派生类进行访问,而无需受到和外部类一样的限制,为此C#允许添加订制的add和remove块,为事件封装的各个组成部分添加自己的实现。可查看Thermostat3类的代码。

public class Thermostat
{
private float currentTemperature; public float CurrentTemperature
{
get { return currentTemperature; }
set
{
if (currentTemperature != value)
{
currentTemperature = value;
/* 1.在这里并没有一开始就检查空值,而是首先将OnTemperatureChange赋值给另一个委托变量localOnChange,这个简单的修改可以确保在检查空值与发送通知之间,假如
* 所有的OnTemperatureChange订阅者都被移除(由一个不同的线程),那么不会触发NullReferenceException异常。
* 2.
*/
var localOnChange = OnTemperatureChange;
if (localOnChange == null)
{
return;
}
//此处的遍历与错误处理,是防止在多播委托执行时,若在委托链中有一个委托方法出现错误,后续的委托方法执行就会中断。
foreach (EventHandler<TemperatureArgs> item in localOnChange.GetInvocationList())
{
try
{
item(this, new TemperatureArgs(value));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
}
//OnTemperatureChange = delegate { };赋值一个空委托,代表零个侦听者构成的集合,就可以引发侦听而不必检查是否有任何侦听者。
public event EventHandler<TemperatureArgs> OnTemperatureChange; /// <summary>
/// 观察者模式示例
/// </summary>
public static void Observer()
{
Adjust heater = new Adjust("heater", 50f, (original, newValue) => original > newValue);
Adjust cooler = new Adjust("cooler", 70f, (original, newValue) => original < newValue);
Thermostat thermostat = new Thermostat();
thermostat.OnTemperatureChange += heater.OnTemperatureChange;
thermostat.OnTemperatureChange += (sender, eArgs) => { throw new ApplicationException(); };
thermostat.OnTemperatureChange += cooler.OnTemperatureChange;
thermostat.CurrentTemperature = 40f;
thermostat.OnTemperatureChange(null, null);
}
} public class TemperatureArgs : EventArgs
{
public TemperatureArgs(float newTemprature)
{
NewTemprature = newTemprature;
} public float NewTemprature { get; set; }
} //C#编译器一旦遇到event关键字,就会生成与Thermostat2类中代码的等价代码的CIL代码。
public class Thermostat2
{
/*private EventHandler<TemperatureArgs> onTemperatureChange; public void add_OnTemperatureChange(EventHandler<TemperatureArgs> handler)
{
Delegate.Combine(onTemperatureChange, handler);
} public void remove_OnTemperatureChange(EventHandler<TemperatureArgs> handler)
{
Delegate.Remove(onTemperatureChange, handler);
} public event EventHandler<TemperatureArgs> OnTemperatureChange
{
add { add_OnTemperatureChange(value); }
remove { remove_OnTemperatureChange(value); }
}*/
} //自定义事件的实现
public class Thermostat3
{
protected EventHandler<TemperatureArgs> onTemperatureChange;
public event EventHandler<TemperatureArgs> OnTemperatureChange
{
add
{
//最后注册的订阅者,在通知时被第一个通知
Delegate.Combine(value, onTemperatureChange);
}
remove { Delegate.Remove(onTemperatureChange, value); }
}
} /// <summary>
/// 调整器,用于控制加热器与制冷器的开关
/// </summary>
public class Adjust
{
public Adjust(string controllerName, float temperature, Func<float, float, bool> predicate)
{
ControllerName = controllerName;
Temperature = temperature;
predicateSwitch = predicate;
}
/// <summary>
/// 控制器名称,可以是制冷器、或加热器
/// </summary>
public string ControllerName { get; set; }
public float Temperature { get; set; }
public Func<float, float, bool> predicateSwitch { get; private set; }
public void OnTemperatureChange(object sender, TemperatureArgs eArgs)
{
Console.WriteLine("{0} : {1}", ControllerName, predicateSwitch(Temperature, eArgs.NewTemprature) ? "On" : "Off");
}
}

C#学习笔记9的更多相关文章

  1. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  2. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  3. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  4. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

  5. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

  6. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

  7. CSS学习笔记

    CSS学习笔记 2016年12月15日整理 CSS基础 Chapter1 在console输入escape("宋体") ENTER 就会出现unicode编码 显示"%u ...

  8. HTML学习笔记

    HTML学习笔记 2016年12月15日整理 Chapter1 URL(scheme://host.domain:port/path/filename) scheme: 定义因特网服务的类型,常见的为 ...

  9. DirectX Graphics Infrastructure(DXGI):最佳范例 学习笔记

    今天要学习的这篇文章写的算是比较早的了,大概在DX11时代就写好了,当时龙书11版看得很潦草,并没有注意这篇文章,现在看12,觉得是跳不过去的一篇文章,地址如下: https://msdn.micro ...

  10. ucos实时操作系统学习笔记——任务间通信(消息)

    ucos另一种任务间通信的机制是消息(mbox),个人感觉是它是queue中只有一个信息的特殊情况,从代码中可以很清楚的看到,因为之前有关于queue的学习笔记,所以一并讲一下mbox.为什么有了qu ...

随机推荐

  1. Java面向对象之内部类(匿名内部类)

    一.基础概念 匿名内部类:简化书写的内部类.其实匿名内部类就是一个子类对象. 前提:内部类需要继承或者实现外部的类或者接口. 格式:new 父类或者接口(){定义子类的内容} 二.将内部类定义到局部的 ...

  2. logback&slf4j学习笔记

    1.Slf4j 1.1.Slf4j简介 SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,它只服务于各种各样的日志系统.实际上,SLF ...

  3. unix网络编程源码编译问题

    ​##获得更多资料欢迎进入我的网站或者 csdn或者博客园 今天在温习计算机网络时,突然将我很久前买的<unix网络编程第三版卷一>拿出来看看了.放了两年了,哈哈.主要讲的套接字,之前编写 ...

  4. 搭建Jenkins--(用于个人学习持续集成)

    ###1.安装java 完成后输入java -version,出现版本信息 输入 javac, 会出现 说明,JDK  和 JRE 安装正确 ###2.安装tomcat 1.下载Tomcat wind ...

  5. Preprefix sum BZOJ 3155 树状数组

    题目描述 前缀和(prefix sum)Si=∑k=1iaiS_i=\sum_{k=1}^i a_iSi​=∑k=1i​ai​. 前前缀和(preprefix sum) 则把SiS_iSi​作为原序列 ...

  6. springcloud微服务总结四 负载均衡

    一:Ribbon简介 Ribbon是Netflix公司开源的一个负载均衡的项目,是一个客户端负载均衡器,运行在客户端上.它是一个经过了云端测试的IPC库,可以很好地控制HTTP和TCP客户端的一些行为 ...

  7. Qt 学习之路 2(62):保存 XML

    Home / Qt 学习之路 2 / Qt 学习之路 2(62):保存 XML Qt 学习之路 2(62):保存 XML  豆子  2013年8月26日  Qt 学习之路 2  9条评论 前面几章我们 ...

  8. 关于Manjaro与Ubuntu双系统并存引发的一个boot问题

    事情发生在写下这篇博客的半小时前.笔者的电脑本身是Manjaro+win10双系统并存,因为一些原因要安装ubuntu. 装完ubuntu用了一阵子,想切回manjaro,于是遇到了这个问题. 看到k ...

  9. POJ_3262 Protecting the Flowers 【贪心】

    一.题面 POJ3262 二.分析 这题要往贪心上面想应该还是很容易的,但问题是要证明为什么比值关系就能满足. 可以选择几个去分析,入1-6  与 2-15  和 1-6 与2-5 和 1-6 与 2 ...

  10. 在Linux系统下用dd命令制作ISO镜像U盘启动盘

    http://os.51cto.com/art/201510/494762.htm 首先在 Linux 系统中打开终端,确认 U 盘路径: sudo fdisk -l 使用 dd 命令,格式如下: s ...