C# 不变还是不不变?
好的,据我所知,不可变类型本质上是线程安全的,或者我在不同的地方读过,我想我理解为什么会这样。如果创建对象后无法修改实例的内部状态,那么并发访问实例本身似乎没有问题 因此,我可以创建以下C# 不变还是不不变?,c#,immutability,C#,Immutability,好的,据我所知,不可变类型本质上是线程安全的,或者我在不同的地方读过,我想我理解为什么会这样。如果创建对象后无法修改实例的内部状态,那么并发访问实例本身似乎没有问题 因此,我可以创建以下列表: class ImmutableList<T>: IEnumerable<T> { readonly List<T> innerList; public ImmutableList(IEnumerable<T> collection)
列表
:
class ImmutableList<T>: IEnumerable<T>
{
readonly List<T> innerList;
public ImmutableList(IEnumerable<T> collection)
{
this.innerList = new List<T>(collection);
}
public ImmutableList()
{
this.innerList = new List<T>();
}
public ImmutableList<T> Add(T item)
{
var list = new ImmutableList<T>(this.innerList);
list.innerList.Add(item);
return list;
}
public ImmutableList<T> Remove(T item)
{
var list = new ImmutableList<T>(this.innerList);
list.innerList.Remove(item);
return list;
} //and so on with relevant List methods...
public T this[int index]
{
get
{
return this.innerList[index];
}
}
public IEnumerator<T> GetEnumerator()
{
return innerList.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return ((System.Collections.IEnumerable)this.innerList).GetEnumerator();
}
}
类不可变列表:IEnumerable
{
只读列表内部列表;
公共不可变列表(IEnumerable集合)
{
this.innerList=新列表(集合);
}
公共不可变列表()
{
this.innerList=新列表();
}
公共不可变列表添加(T项)
{
var list=新的不可变列表(this.innerList);
list.innerList.Add(项);
退货清单;
}
公共不可变列表删除(T项)
{
var list=新的不可变列表(this.innerList);
list.innerList.Remove(项);
退货清单;
}//依此类推,并使用相关的列表方法。。。
公共T此[int索引]
{
得到
{
返回此.innerList[索引];
}
}
公共IEnumerator GetEnumerator()
{
返回innerList.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return((System.Collections.IEnumerable)this.innerList).GetEnumerator();
}
}
所以问题是:这真的是一个不可变的类型吗?它真的是线程安全的吗
很明显,类型本身是不可变的,但是绝对没有保证T
是不变的,因此您可能会遇到与泛型类型直接相关的并发访问和线程问题。这是否意味着ImmutableList
应该被视为可变的
类ImmutableList:IEnumerable其中T:struct
是否应该是唯一真正被认为是不可变的类型
感谢您对这个问题的任何意见
更新:很多答案/评论都集中在我发布的不可变列表
的具体实现上,这可能不是一个很好的例子。但问题不在于实施。我要问的问题是,考虑到不可变类型所包含的一切,不可变列表是否真的是不可变类型
如果创建对象后无法修改实例的内部状态,那么并发访问实例本身似乎没有问题
通常情况就是这样,是的
这真的是一个不可变的类型吗
简单地总结一下:在可变列表周围有一个“写上拷贝”包装器。向不可变列表中添加新成员不会改变该列表;相反,它创建基础可变列表的副本,添加到副本中,并返回副本周围的包装器
如果您正在包装的基础列表对象在读取时没有改变其内部状态,那么您已经满足了“不可变”的原始定义,因此,是的
我注意到,这不是实现不可变列表的非常有效的方法。例如,使用不可变平衡二叉树可能会做得更好。每次你做一个新的列表,你的草图在时间和记忆上都是O(n);您可以不费吹灰之力地将其改进为O(logn)
它真的是线程安全的吗
如果基础可变列表对于多个读卡器是线程安全的,则可以
您可能对此感兴趣:
显然,类型本身是不可变的,但绝对不能保证T是不变的,因此您可能会遇到与泛型类型直接相关的并发访问和线程问题。这是否意味着ImmutableList
应该被视为可变的
这是一个哲学问题,不是技术问题。如果你有一个不变的人名列表,而这个列表从不改变,但是其中一个人死了,那么这个人名列表是“可变的”吗?我想不会
如果关于列表的任何问题总是有相同的答案,那么列表是不可变的。在我们的名单中,“名单上有多少人?”是关于名单的问题。“这些人中有多少人还活着?”不是关于名单的问题,而是关于名单所指的人的问题。这个问题的答案会随着时间的推移而改变;第一个问题的答案并非如此
classImmutableList:IEnumerable其中T:struct
应该是唯一真正被认为是不可变的类型吗
我不明白你的意思。将T限制为结构如何改变任何事情?好的,T被限制为struct。我创建了一个不可变的结构:
struct S
{
public int[] MutableArray { get; private set; }
...
}
现在我制作了一个
不可变列表
。是什么阻止我修改存储在S实例中的可变数组?仅仅因为列表是不可变的,结构是不可变的,并不意味着数组是不可变的。不可变性有时是以不同的方式定义的。线程安全也是如此
在创建一个目的是不可变的不可变列表时,您应该记录您所做的保证。例如,在这种情况下,您可以保证列表本身是不可变的,并且没有任何隐藏的易变性(一些表面上不可变的对象在幕后实际上是可变的,例如,作为优化的记忆或内部重新排序),这将消除来自不可变性的线程安全性(虽然也可以以另一种方式保证线程安全的方式执行此类内部突变)。您不能保证存储的对象可以以线程安全的方式使用
您应该记录的线程安全性与此相关。您不能保证另一个对象不会具有相同的对象(如果每次调用时都创建新对象,则可以)。您可以保证操作不会损坏列表本身
坚持使用T:struct
可能会有所帮助,因为这意味着您可以确保
ImmutableList<int> mylist = new ImmutableList<int>();
mylist.Add(1);