为什么委托的减法(- 或 -=)可能出现非预期的结果?(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 ...
随机推荐
- lucene介绍和存储介绍
全文检索基础 1. Windows系统中的有搜索功能:打开“我的电脑”,按“F3”就可以使用查找的功能,查找指定的文件或文件夹.搜索的范围是整个电脑中的文件资源. 2. 在BBS.BLOG.新闻等系统 ...
- shell基本认识
shell基本认识 bash # echo $BASH /bin/bash 第一个shell脚本first_shell.sh #!/bin/bash echo "Hello world!&q ...
- GDB操作基本命令
GDB操作基本命令 1 打开文件及退出操作 shell下输入 gdb filename : 打开单个文件 gdb filename -q: 打开单个文件,屏蔽掉GDB自带的说明信息 gdb下输入qui ...
- LeetCode第[78]题(Java):Subsets(求子集)扩展——第[90]题:Subsets 2
题目:矩阵置0 难度:Easy 题目内容: Given a set of distinct integers, nums, return all possible subsets (the pow ...
- 动态延迟加载网页元素jQuery插件scrollLoading
如果一个网页很长,那么该页面的加载时间也会相应的较长.而这里给大家介绍的这个jQuery插件scrollLoading的作用则是,对页面元素进行动态加载,通俗的说就是滚到哪就加载到哪,屏幕以下看不见的 ...
- .Net Core 修改默认的启动端口
今天无意中发现一个变化,因为很久没看.net core的项目了,发现项目启动的默认端口已经不是5000了,记得很清楚,最早那还是.net core 1.x版本的时候,每次启动都会默认是5000端口号, ...
- Selenium入门练习(二)
自动登录博客园并且退出登录 package TestNG; import org.testng.annotations.Test;import org.testng.annotations.Befor ...
- python脚本10_打印斐波那契数列的第101项
#打印斐波那契数列的第101项 a = 1 b = 1 for count in range(99): a,b = b,a+b else: print(b) 方法2: #打印斐波那契数列的第101项 ...
- Android Studio 3.0 及个版本下载和 gradle 各版本下载
Android Studio 3.0 下载地址: 链接:http://pan.baidu.com/s/1jHVuOQi 密码:3pd0 Android Studio 3.0 包含了三大主要功能: 一套 ...
- 服务器22端口连接超时 ssh: connect to host *** port 22: Operation timed out
最近酸酸乳出问题,连接v社服务器发现碰到 ssh: connect to host master port 22: Connection timed out 的问题.现在对该问题做一下可能出现的问题 ...