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

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


当前回答

我不会说可变性是邪恶的,但可变性通常是程序员急于提供最大功能的标志。在现实中,这通常是不需要的,反过来,使界面更小,更容易使用,更难使用错误(=更健壮)。

其中一个例子就是竞态条件中的读/写和写/写冲突。这些在不可变结构中根本不可能发生,因为写操作不是有效的操作。

另外,我认为可变性几乎从来都不是真正需要的,程序员只是认为它在未来可能会出现。例如,改变日期是没有意义的。相反,在旧日期的基础上创建一个新的日期。这是一个廉价的操作,所以性能不是一个考虑因素。

其他回答

从哪里开始;-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是不可变的,那么所有通过值传递的自动副本都是相同的。

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

我不会说可变性是邪恶的,但可变性通常是程序员急于提供最大功能的标志。在现实中,这通常是不需要的,反过来,使界面更小,更容易使用,更难使用错误(=更健壮)。

其中一个例子就是竞态条件中的读/写和写/写冲突。这些在不可变结构中根本不可能发生,因为写操作不是有效的操作。

另外,我认为可变性几乎从来都不是真正需要的,程序员只是认为它在未来可能会出现。例如,改变日期是没有意义的。相反,在旧日期的基础上创建一个新的日期。这是一个廉价的操作,所以性能不是一个考虑因素。

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

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

正确的实现如下所示。

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. 按任意键继续…

可变结构体并不邪恶。

在高绩效环境下,它们是绝对必要的。例如,当缓存线和垃圾收集成为瓶颈时。

我不认为在这些完全有效的用例中使用不可变结构体是“邪恶的”。

我可以看到c#的语法没有帮助区分值类型或引用类型的成员的访问,所以我完全赞成使用强制不变性的不可变结构,而不是可变结构。

然而,与其简单地给不可变结构贴上“邪恶”的标签,我建议接受这种语言,提倡更有帮助和建设性的经验法则。

例如:"struct是默认复制的值类型。如果你不想复制他们,你需要一份推荐信。 “首先尝试使用只读结构体”。