Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 不变还是不不变?_C#_Immutability - Fatal编程技术网

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
应该被视为可变的

这是一个哲学问题,不是技术问题。如果你有一个不变的人名列表,而这个列表从不改变,但是其中一个人死了,那么这个人名列表是“可变的”吗?我想不会

如果关于列表的任何问题总是有相同的答案,那么列表是不可变的。在我们的名单中,“名单上有多少人?”是关于名单的问题。“这些人中有多少人还活着?”不是关于名单的问题,而是关于名单所指的人的问题。这个问题的答案会随着时间的推移而改变;第一个问题的答案并非如此

class
ImmutableList: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);