在C#中,值类型是可变的还是不可变的?

时间:2011-08-17 04:50:39

标签: c# value-type reference-type

值类型行为表明,无论我们持有什么值,都无法通过其他变量进行更改。

但是我仍然对我在这篇文章标题中提到的内容感到困惑。任何人都可以澄清吗?

3 个答案:

答案 0 :(得分:21)

值类型可以是可变的(以一些奇怪的边缘情况为模)不可变,具体取决于你如何编写它们。

可变的:

public struct MutableValueType
{
    public int MyInt { get; set; }
}

不可变:

public struct ImmutableValueType
{
    private readonly int myInt;
    public ImmutableValueType(int i) { this.myInt = i; }

    public int MyInt { get { return this.myInt; } }
}

内置值类型(intdouble等)不可变的,但您可以非常轻松地创建自己的可变struct

一条建议:不要。可变值类型是一个坏主意,应该避免。例如,这段代码的作用是什么:

SomeType t = new SomeType();
t.X = 5;

SomeType u = t;
t.X = 10;

Console.WriteLine(u.X);

取决于。如果SomeType是值类型,则会打印5,这是一个非常令人困惑的结果。

有关为何应避免使用可变值类型的详细信息,请参阅this question

答案 1 :(得分:3)

所有原始值类型(如int,double,float)都是不可变的。但是结构本身是可变的。所以你必须采取措施使它们成为不可变的,因为它会造成很多混淆。

答案 2 :(得分:1)

任何保存任何信息的值类型实例都可以通过代码进行变异,该代码可以编写包含它的存储位置,并且没有值类型实例可以被不能写入包含它的存储位置的代码变更。这些特性使得可变值类型的私有存储位置在许多场景中成为理想的数据容器,因为它们将源自可变性的更新便利性与来自不变性的控制相结合。请注意,可以编写值类型的代码,使得在没有包含所需数据的实例(可能是新创建的临时实例)并且覆盖内容的情况下,不可能改变现有实例。以前的实例与后者的内容,但这不会使价值类型变得更多或更少可变,而不是没有这种能力。在许多情况下,它只会使突变变得尴尬,并使其看起来像一个如下声明:

  MyKeyValuePair =
    new KeyValuePair<long,long>(MyKeyValuePair.Key+1, MyKeyValuePair.Value+1>;

将创建一个新实例,但不会影响现有实例。如果KeyValuePair是一个不可变类,并且一个线程执行MyKeyValuePair.ToString()而另一个线程正在执行上述代码,则ToString调用将对旧实例或新实例执行操作,并且从而产生旧值或两个新值。但是因为KeyValuePair是一个结构,上面的语句将创建一个新实例,但它不会使MyKeyValuePair引用新实例 - 它只会使用新实例作为模板,字段将复制到MyKeyValuePair。如果KeyValuePair是一个可变结构,那么上述代码可能意图含义的最自然表达式更像是:

  MyKeyValuePair.Key += 1;
  MyKeyValuePair.Value += 1;

或者也许:

  var temp = MyKeyValuePair;
  MyKeyValuePair.Key = temp.Key+1;
  MyKeyValuePair.Value = temp.Value+1;

并且线程含义会更清晰。