包含可变引用类型成员的值类型是否被视为不可变?

时间:2014-03-13 08:34:18

标签: c#

我知道mutable structs are evil。但请考虑以下struct

public struct Foo
{
    private readonly StringBuilder _sb;

    public Foo(StringBuilder sb)
    {
        _sb = sb;
    }

    public StringBuilder Sb
    {
        get { return _sb; }
    }
}

对我而言,Foo似乎是不可变的,因为当您执行var foo = new Foo(new StringBuilder())时,您无法重新分配Sb属性,因为它是只读的。

但是你可以这样做:

var foo = new Foo(new StringBuilder());
foo.Sb.Append("abc"); // _sb is also modified

会影响foo的内部状态。

问题:这被认为是一个很好的struct设计还是应该避免在值类型中使用可变引用类型成员?

2 个答案:

答案 0 :(得分:4)

这被称为浅不变性。

看看Joe Duffy的Solving 11 Likely Problems In Your Multithreaded Code(强调我的)的不变性部分:

  

例如,只有只读字段的.NET类型是浅不可变。 (...)更进一步,如果每个字段本身引用其字段都是只读的另一种类型(并且仅指深度不可变类型),则类型深度不可变 。这会导致整个对象图保证不会从您下面改变,这确实非常有用。

如果你的结构是不可变的,以防止客户端试图修改作为参数传递的结构的副本,而不是原始值,那么浅不变性就好了。原始值和副本都将指向相同的StringBuilder

但是,如果你是为同步目的而做的话,那就不会有用了。

来自Eric Lippert的Immutability in C# Part One: Kinds of Immutability

  

真正疯狂不可变的物体有很多很棒的属性。例如,它们是100%线程安全的,因为很明显读者和(不存在的)作者之间不会有冲突。它们比可以改变的对象更容易推理。但是他们的严格要求可能超出我们的要求,或者超出实际可行的要求。

答案 1 :(得分:1)

您的设计规则是由您和其他人明确理解您的选择的能力驱动的。

这是我的:我尽量保持我的结构保持简单的化合物元素像Vectors,Points,并且只使用不应单独更改的值类型。

如果我需要在结构中使用集合或引用类型来暴露可能会改变其状态的成员,我将其作为私有,并且我将函数方法包装起来以获取内容。因为我必须防止它从外面修改。

如果我的结构中的某些东西必须被修改,我认为结构不是我需要的。

同样,没有什么能阻止你通过使用像这样的浅不变性来欺骗结构。 dcastro的回答刚刚了解了这是怎么称呼的。

然而,因为结构不是那样工作的,它可能会让IMHO感到困惑。

我喜欢这个Eric Lippert's article让我想起结构应该如何工作。