不可变还是不可变?好吧,据我所知,不可变类型本质上是线程安全的,所以我在各个地方都读过,我想我明白为什么会这样。如果实例的内部状态在对象创建后无法修改,那么并发访问实例本身似乎没问题。所以我可以创建以下列表:classImmutableList:IEnumerable{readonlyListinnerList;publicImmutableList(IEnumerablecollection){this.innerList=newList(collection);}publicImmutableList(){this.innerList=newList();}publicImmutableListAdd(Titem){varlist=newImmutableList(this.innerList);list.innerList.Add(项目);返回列表;}publicImmutableListRemove(Titem){varlist=newImmutableList(this.innerList);list.innerList.Remove(item);返回列表;}//等等相关的List方法...publicTthis[intindex]{get{returnthis.innerList[index];}}publicIEnumeratorGetEnumerator(){returninnerList.GetEnumerator();}System.Collections.IEnumeratorSystem.Collections.IEnumerable.GetEnumerator(){返回((System.Collections.IEnumerable)this.innerList).GetEnumerator();那么问题来了:this真的是不可变类型吗?它真的是线程安全的吗?显然类型本身是不可变的,但绝对不能保证T是不可变的,因此您可以直接处理与泛型相关的并发访问和线程问题。这是否意味着ImmutableList应该被认为是可变的?classImmutableList:IEnumerable其中T:struct是唯一真正被认为是不可变的类型?感谢您对此问题的任何意见。更新:很多答案/评论都集中在我发布的ImmutableList的具体实现上,这可能不是一个很好的例子。但有问题的不是执行。我要问的问题是,考虑到不可变类型所需的一切,ImmutableList是否真的是不可变类型。如果实例的内部状态在对象创建后无法修改,那么并发访问实例本身似乎没问题。通常是这样,是的。这真的是不可变类型吗?简而言之:您有一个围绕可变列表的写时复制包装器。向不可变列表添加新成员不会改变列表;相反,它创建底层可变列表的副本,添加到副本,并返回副本周围的包装器。如果您包装的底层列表对象在读取时没有改变其内部状态,那么您已经满足了“不可变”的原始定义,所以,是的。我注意到这不是实现不可变列表的非常有效的方法。例如,使用不可变的平衡二叉树可能会做得更好。每当你创建一个新列表时,你的草图在时间和内存上都是O(n);您可以毫不费力地将其改进为O(logn)。它真的是线程安全的吗?只要底层可变列表对多个读者来说是线程安全的,就可以。您可能对此感兴趣:http://blogs.msdn.com/b/ericlippert/archive/2011/05/23/read-only-and-threadsafe-are-different.aspx显然类型本身是不可变的是的,但绝对不能保证T是,因此您可以直接处理与泛型相关的并发访问和线程问题。这是否意味着ImmutableList应该被认为是可变的?这是一个哲学问题,不是技术问题。如果你有一个不可变的人名列表,并且列表永远不会改变,但是其中一个人死了,那么这个人名列表是“可变的”吗?我不这么认为。如果关于列表的任何问题总是有相同的答案,则列表是不可变的。在我们的名单中,“名单上有多少人?”是关于列表的问题。“这些人中还有多少人还活着?”不是关于名单的问题,而是关于名单中提到的人的问题。这个问题的答案随着时间而改变;第一个问题的答案没有。类ImmutableList:IEnumerablewhereT:struct是唯一真正被认为是不可变的类型吗?我没有跟着你。将T限制为结构如何改变任何事情?好的,T仅限于struct。我创建了一个不可变结构:structS{publicint[]MutableArray{get;私有集;}...}现在我创建了一个ImmutableList。是什么阻止我修改存储在S实例中的可变数组?仅仅因为列表是不可变的并且结构是不可变的并不会使数组不可变。不变性有时以不同的方式定义。线程安全也是如此。当创建一个旨在不可变的不可变列表时,您应该记录您所做的保证。例如,在这种情况下,您可以保证列表本身是不可变的,并且没有任何隐藏的可变性(一些表面上不可变的对象实际上在幕后是可变的,例如作为优化的记忆或内部重新排序),这消除了来自不变性的线程安全(尽管也可以以不同方式保证线程安全的方式执行此类内部突变)。您无法保证可以以线程安全的方式使用存储的对象。您应该记录的线程安全与此有关。您不能保证另一个对象不会有相同的对象(如果您在每次调用时都创建新对象,则可以)。您可以保证该操作不会破坏列表本身。坚持使用T:struct会有所帮助,因为这意味着您可以确定每次返回一个项目时,它都是该结构的一个新副本(T:struct本身不会这样做,因为您可能有执行'does'改变列表,但它确实改变了它的成员,所以显然你必须做同样的事情)。这虽然限制了您支持不可变引用类型(例如,在许多实际情况下往往是集合成员的字符串),并且不允许用户使用它并提供他们自己的方法来确保所包含项目的可变性不不会引起问题。由于没有线程安全对象可以保证它使用的所有代码都是线程安全的,因此请确保(尽可能多,但不要试图确保您不能确定)。它也不保护不可变列表中不可变结构的可变成员!使用您的代码,假设我这样做:ImmutableListmylist=newImmutableList();mylist.Add(1);...StackOverflow上发布的代码导致StackOverflow异常。有很多明智的方法可以创建线程持有集合、复制集合(至少尝试)并称它们为不可变的,很多,但效率不高。EricLippert发布的链接可能值得一读。其行为是可变对象的不可变列表的数据类型的主要示例:MulticastDelegate。MulticastDelegate可以非常准确地建模为(对象,方法)对的不可变列表。方法集和它们所作用的对象的身份是不可变的,但在绝大多数情况下,对象本身是可变的。事实上,在许多(如果不是大多数)情况下,委托的目的是更改它所引用的对象。委托者没有责任知道它即将对其目标对象调用的方法是否会以线程不安全的方式改变它们。委托只负责确保其功能和对象ID列表是不可变的,我认为没有人期望相同。同样,ImmutableList应该始终持有同一组T类型的实例。这些实例的属性可能会改变,但它们的身份不会改变。如果创建一个列表,其中包含序列号为#1234和#4422的两辆福特汽车,均为红色,其中一辆涂成蓝色,则两辆红色汽车的初始列表将更改为蓝色汽车列表和红色汽车列表汽车,但它仍会保留#1234和#4422。我会说,如果元素是可变的,泛型列表就不是不可变的,因为它不代表数据状态的完整快照。要在您的示例中实现不变性,您必须创建列表元素的深层副本,这并非每次都有效。你可以在IOG库中看到我对这个问题的解决方案上面是C#学习教程:不可变还是不可变?如果所有分享的内容对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
