为什么委托的减法(- 或 -=)可能出现非预期的结果?(Delegate Subtraction Has Unpredictable Result)
当我们为一个委托写 -= 的时候,ReSharper 会提示“Delegate Subtraction Has Unpredictable Result”,即“委托的减法可能出现非预期的结果”。然而在写为事件写 -= 的时候却并没有这样的提示。然而这个提示是什么意思呢?为什么会“非预期”?为什么委托会提示而事件不会提示?
阅读本文将了解委托的减法。
▲ 委托的减法可能出现非预期的结果
ReSharper 的官方帮助文档
例子和现象
从 ReSharper 的提示中,我们可以跳转到官方帮助文档 Code Inspection: Delegate subtractions - Help - ReSharper。
官方文档中给出了一个非常典型的 Demo 程序:
static void Main()
{
Action a = () => Console.Write("A");
Action b = () => Console.Write("B");
Action c = () => Console.Write("C");
Action s = a + b + c + Console.WriteLine;
s(); //ABC
(s - a)(); //BC
(s - b)(); //AC
(s - c)(); //AB
(s - (a + b))(); //C
(s - (b + c))(); //A
(s - (a + c))(); //ABC
}
关键就在最后一行的输出结果。由于 s 等于 a + b + c,s - (a + c) 却依然输出 ABC,而不是前面例子中就像数学加减法一样的输出。
ReSharper 同时还给出另一个例子,说明委托的减法顺序也可能非预期:
s = a + b + a;
(s - a)(); // AB
它会从尾部减起,而这一点也容易被大家忽视。
原理
究其原因,ReSharper 官方文档也已说明。因为委托保存了一个调用列表,委托的 a + b,是将 b 的调用列表追加到 a 的调用列表之后;而委托的 a - b 是从 a 的调用列表中移除 b 的调用列表子序列。
也就是说,委托的加减其实就是委托调用列表中序列的拼接和子序列的移除。
用图来表示这个调用列表的加减过程,可以画成这样。其中 a, b 是委托,x, y, z, w 是调用列表中的每一项。
▲ 调用列表的加减其实就是序列的拼接和子序列的移除
将委托和事件比较
既然 ReSharper 对委托做出了这样的提示,而事件几乎就是委托的封装,那为何事件不给出提示呢?!
带着疑问,我将 ReSharper 官方例子中的 s 改成了事件,其他代码完全一样。
private static event Action s;
static void Main()
{
Action a = () => Console.Write("A");
Action b = () => Console.Write("B");
Action c = () => Console.Write("C");
// 这一句注释掉,因为 s 换成了事件,而事件必须定义在类中。
// Action s = a + b + c + Console.WriteLine;
s(); //ABC
(s - a)(); //BC
(s - b)(); //AC
(s - c)(); //AB
(s - (a + b))(); //C
(s - (b + c))(); //A
(s - (a + c))(); //ABC
}
后面用于代表输出结果的注释我依然没改,因为输出结果真的没变!!!也就是说,理论上使用事件并不能帮助减少委托减法带来的结果不确定性。
但是——事件是观察者模式的一种实现,从设计上说,事件只作通知之用,不确保顺序,也不保证结果。在这个角度上说,如果依然用事件写出上面 demo 那样的“不可预期”代码,那简直不把事件当事件用。
不再用委托减法了吗?
至少从设计模式上说,事件里委托减法的的那些非预期就忽略吧,那么没有定义成事件的那些委托呢?我们需要如何处理减法?
其实,大可不必太担心,因为大多数场合下我们进行委托加法和减法时,都是用一个包含调用列表的委托与其它只有一个调用节点的委托进行加减,通常结果都是符合预期的,也通常不会对顺序敏感。但是,如果委托的减法是库 API 的一部分,那就需要小心,因为库的使用者可能写出任何一种诡异的代码!这种情况下,换成事件是一个不错的选择。
参考资料
- Code Inspection: Delegate subtractions - Help - ReSharper
- events - “Delegate subtraction has unpredictable result” in ReSharper/C#? - Stack Overflow
为什么委托的减法(- 或 -=)可能出现非预期的结果?(Delegate Subtraction Has Unpredictable Result)的更多相关文章
- [改善Java代码] 避免instanceof非预期结果
建议18: 避免instanceof非预期结果 instanceof是一个简单的二元操作符,它是用来判断一个对象是否是一个类实例的,其操作类似于>=.==,非常简单,我们来看段程序,代码如下: ...
- int.TryParse非预期执行引发的思考 ASP.NET -- WebForm -- 给图片添加水印标记 Windows -- 使用批处理文件.bat删除旧文件
int.TryParse非预期执行引发的思考 问题出现 这天在写一个页面,想谨慎些就用了int.TryParse,结果出问题了. 代码如下: Copy int id = 1000; //Reque ...
- int.TryParse非预期执行引发的思考
问题出现 这天在写一个页面,想谨慎些就用了int.TryParse,结果出问题了. 代码如下: int id = 1000; //Request.QueryString["id"] ...
- MySQL时间盲注五种延时方法 (PWNHUB 非预期解)
转自cdxy师傅:https://www.cdxy.me/?p=789 PWNHUB 一道盲注题过滤了常规的sleep和benchmark函数,引发对时间盲注中延时方法的思考. 延时函数 SLEEP ...
- C语言的可变参数在Linux(Ubuntu)与Windows下注意点
基本上C语言的可变参数原理在不同平台和不同编译器下基本类似(通过函数入栈,从右向左,从高位到低位地址),不过部分实现会有所不同:在使用中需要注意的是: va_list 为char 类型指针,部分调用如 ...
- Hibernate的查询语言之HQL(二)——Hibernate查询的from字句
from 是最简单的HQL语句,也是最基本的HQL语句.from 关键字后紧跟持久化类的类名.例如: from Person 表明从Person持久化类中取出全部的实例. 大部分时候,推荐位该Pers ...
- [Object Tracking] Overview of algorithms for Object Tracking
From: https://www.zhihu.com/question/26493945 可以载入史册的知乎贴 目标跟踪之NIUBILITY的相关滤波 - 专注于分享目标跟踪中非常高效快速的相关滤波 ...
- 【算法基础】卡尔曼滤波KF
kalman filter KCF 尺度变化是跟踪中比较基本和常见的问题,前面介绍的三个算法都没有尺度更新,如果目标缩小,滤波器就会学习到大量背景信息,如果目标扩大,滤波器就跟着目标局部纹理走了,这两 ...
- CTF_show平台 web题解 part3
web13 题目显示文件上传,各类型上传均提示错误,在使用ctf-scan扫描的时候,发现upload.php.bak. 查看源码: <?php header("content-typ ...
随机推荐
- location的部分属性
http://www.w3school.com.cn/jsref/dom_obj_location.asp location.host 可以设置或返回主机名和当前url的端口 www.w3sch ...
- VS2012 QT5.2.0 无法解析的外部符号
背景:在新建QT工程时,可能没有选择一些库,虽然在头文件中引用了,但是程序依然无法识别 现象:一般出现"LNK2019"错误. 解决:以网络为例,在VS2012中加入网络库,分为两 ...
- C++名字查找和重载
重载函数的定义:在同一作用域内的几个函数名字相同但形参列表不同,称为重载函数.这里有一个重要的前提就是:同一个作用域: 而如果重载函数是定义在不同的作用域,那么一旦编译器在当前作用域找到所需的名字,编 ...
- pfSense 2.4.3 发布,包含重要的安全修复补丁
pfSense 2.4.3 已发布,本次更新包含重要的安全修复和 bug 修复,还引入了一些新特性,具体如下. 值得关注的更新 包含一些重要的安全修复补丁: Kernel PTI mitigation ...
- 十六 web爬虫讲解2—PhantomJS虚拟浏览器+selenium模块操作PhantomJS
PhantomJS虚拟浏览器 phantomjs 是一个基于js的webkit内核无头浏览器 也就是没有显示界面的浏览器,利用这个软件,可以获取到网址js加载的任何信息,也就是可以获取浏览器异步加载的 ...
- nyoj35——逆波兰表达式
逆波兰表达式又称作后缀表达式,在四则混合运算的程序设计中用到. 例如: 1+2写成后缀表达式就是12+ 4+5*(3-2)的后缀表达式就是4532-*+ 后缀表达式在四则运算中带来了意想不到的方便,在 ...
- Linux 系统启动过程,Linux 系统目录结构
一.Linux 系统启动过程 linux启动时我们会看到许多启动信息. Linux系统的启动过程并不是大家想象中的那么复杂,其过程可以分为5个阶段: 内核的引导. 运行 init. 系统初始化. 建立 ...
- jquery基础 笔记一
一. 1. vsdoc: 在Visual Studio中需要引入此版本的jquery类库才能启用智能感知.如:jquery-1.3.2-vsdoc2.js<body> <div id ...
- 拒绝了对对象 'sp_OACreate' (数据库 'mssqlsystemresource',架构 'sys')的 EXECUTE 权限。
执行一个存储过程, 由于里面使用到了一些 --创建对象 EXEC sp_OACreate 'VBScript.RegExp', @objRegex OUT --设置属性 EXEC sp_OASe ...
- vue 侧边导航栏递归显示
import axios from "axios"; import tabs1 from "../tab_content/tab1.vue"; import m ...