C#中委托不可变的目的是什么?

8
我正在阅读一本名为《C# 2012图解》的书,其中有一个关于合并委托的章节。你是否注意到了?委托的目的是不可变的。
合并委托 到目前为止,你所看到的所有委托都只有一个方法在其调用列表中。可以通过使用加法运算符来“合并”委托。操作的结果是创建一个新的委托,其调用列表是两个操作数委托的副本串联而成。例如,以下代码创建了三个委托。第三个委托是由前两个委托组合而成的。
MyDel delA = myInstObj.MyM1;
MyDel delB = SClass.OtherM2;
MyDel delC = delA + delB; // Has combined invocation list

尽管“组合委托”这个术语可能会让人误以为操作数委托被修改了,但它们实际上并没有改变。事实上,委托是不可变的。创建委托对象后,它不能被更改。图15-6说明了前面代码的结果。请注意,操作数委托保持不变。

enter image description here


委托实际上是方法指针。您建议对这样的委托进行变异应该怎么做?修改基础方法? - Yuval Itzchakov
1
@YuvalItzchakov 嗯,可能原帖的作者认为它们可以是可变的,所以将两个委托相加会重用一个委托实例并将另一个委托添加到调用列表中...谁知道呢。 - Matías Fidemraizer
2个回答

11

线程安全性和速度是主要问题。更新委托不是原子操作,需要更新6个字段和一个列表。为了使它不会被破坏,需要使用锁定,这对于很少需要保证线程安全的基本操作来说太昂贵了。

通过使其不可变,委托对象就不会被破坏,因为它总是在一个没有任何引用的对象上设置字段。重新分配委托对象引用是原子的,这是.NET内存模型的基本保证。所以不再需要锁定。代价是更低效的内存使用,这是一种小的惩罚。

请注意,线程安全的委托更新并不能自动使您的代码线程安全。测试和触发代码需要复制引用以避免NRE,并且您可能会回调到一个已经取消订阅的方法。


1

代理是指向方法的指针,可以是具体的方法或匿名方法(即使匿名方法也编译有一些编译器生成的标识符)。

将指向某个具体物体的东西变为可变是合理的吗?我认为不是。每个实例都代表着指向某个方法的指针。想要指向另一个方法吗?创建一个新的指针。也就是说,您要实例化一个新的代理。

另一方面,两个或多个代理的添加会产生这样的结果,因为+运算符可以被重载。也就是说,代理可以成为加法的一部分,但在内部,+的重载正在创建一个具有调用列表的新代理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接