在这里的SO讨论之后,我已经多次看到这样的评论,即可变结构体是“邪恶的”(就像在这个问题的答案中)。

c#中的可变性和结构的实际问题是什么?


当前回答

可变数据有许多优点和缺点。最大的缺点就是别名。如果相同的值在多个地方使用,其中一个地方更改了它,那么它将神奇地更改到正在使用它的其他地方。这与竞态条件有关,但并不完全相同。

有时候,价值百万美元的优势是模块化。可变状态允许您向代码隐藏更改的信息,而代码不需要知道这些信息。

《解释器的艺术》详细讨论了这些权衡,并给出了一些例子。

其他回答

如果你曾经用C/ c++这样的语言编程,结构体可以作为可变的。只要把球传给裁判,没有什么会出错的。我发现的唯一问题是c#编译器的限制,在某些情况下,我无法强迫这个愚蠢的东西使用对结构的引用,而不是Copy(比如当结构是c#类的一部分时)。

所以,可变结构体不是邪恶的,是c#把它们变成了邪恶的。我一直在c++中使用可变结构体,它们非常方便和直观。相比之下,c#让我完全放弃了作为类成员的结构体,因为它们处理对象的方式。他们的便利让我们付出了代价。

它与结构无关(也与c#无关),但在Java中,当可变对象是哈希映射中的键时,你可能会遇到问题。如果你在将它们添加到映射后更改它们,它也更改了哈希代码,可能会发生糟糕的事情。

从哪里开始;-p

埃里克·利珀特的博客总是很适合引用:

这是可变的另一个原因 值类型是邪恶的。试着总是 使值类型不可变。

首先,您很容易丢失更改……例如,从列表中获取内容:

Foo foo = list[0];
foo.Name = "abc";

这改变了什么?没有什么有用的…

属性也是一样:

myObj.SomeProperty.Size = 22; // the compiler spots this one

强迫你做:

Bar bar = myObj.SomeProperty;
bar.Size = 22;
myObj.SomeProperty = bar;

不那么关键的是规模问题;可变对象往往有多个属性;然而,如果你有一个包含两个int型,一个string型,一个DateTime型和一个bool型的结构体,你会很快消耗大量内存。使用类,多个调用方可以共享对同一个实例的引用(引用很小)。

李柏特先生举的例子有几个问题。它是为了说明结构是复制的,以及如果你不小心的话,这可能是一个问题。看看这个例子,我认为这是一个坏的编程习惯的结果,而不是结构或类的问题。

结构应该只有公共成员,不需要任何封装。如果是这样,那么它真的应该是一个类型/类。你真的不需要两个构念来表达同一件事。 如果有一个类包含一个结构,则可以调用该类中的一个方法来更改成员结构。我认为这是一个良好的编程习惯。

正确的实现如下所示。

struct Mutable {
public int x;
}

class Test {
    private Mutable m = new Mutable();
    public int mutate()
    { 
        m.x = m.x + 1;
        return m.x;
    }
  }
  static void Main(string[] args) {
        Test t = new Test();
        System.Console.WriteLine(t.mutate());
        System.Console.WriteLine(t.mutate());
        System.Console.WriteLine(t.mutate());
    }

看起来这是编程习惯的问题,而不是结构本身的问题。结构体应该是可变的,这是它的思想和意图。

更改的结果voila表现如预期:

1 2 3. 按任意键继续…

结构是值类型,这意味着它们在传递时被复制。

所以如果你改变了一份副本,你只是改变了那份副本,而不是原件,也不是周围可能存在的其他副本。

如果你的struct是不可变的,那么所有通过值传递的自动副本都是相同的。

如果你想要改变它,你必须有意识地用修改过的数据创建一个结构的新实例。(非副本)