C# 如何使结构不可变?
在堆栈溢出和internet上,我发现保持结构不变是一个很好的设计原则。不幸的是,我从来没有看到任何实际导致这些结构真正不可变的实现 假设一个结构里面没有任何引用类型,我如何使一个结构不可变呢?也就是说,如何防止其任何基元字段发生变异(可能是由于编译时/运行时异常) 我编写了一个简单的测试,试图使结构不可变,但甚至没有使用C# 如何使结构不可变?,c#,struct,immutability,C#,Struct,Immutability,在堆栈溢出和internet上,我发现保持结构不变是一个很好的设计原则。不幸的是,我从来没有看到任何实际导致这些结构真正不可变的实现 假设一个结构里面没有任何引用类型,我如何使一个结构不可变呢?也就是说,如何防止其任何基元字段发生变异(可能是由于编译时/运行时异常) 我编写了一个简单的测试,试图使结构不可变,但甚至没有使用System.ComponentModel.ImmutableObjectAttribute工作: class Program { static void Main(s
System.ComponentModel.ImmutableObjectAttribute
工作:
class Program
{
static void Main(string[] args)
{
ImmutableStruct immStruct1 = new ImmutableStruct();
Console.WriteLine(immStruct1); //Before mutation.
immStruct1.field1 = 1;
immStruct1.field2 = "Hello";
immStruct1.field3 = new object();
Console.WriteLine(immStruct1); //After 1st mutation.
immStruct1.field1 = 2;
immStruct1.field2 = "World";
immStruct1.field3 = new object();
Console.WriteLine(immStruct1); //After 2nd mutation.
Console.ReadKey();
}
}
[ImmutableObject(true)]
struct ImmutableStruct
{
public int field1;
public string field2;
public object field3;
public override string ToString()
{
string field3String = "null";
if (field3 != null)
{
field3String = field3.GetHashCode().ToString();
}
return String.Format("Field1: {0}, Field2: {1}, Field3: {2}", field1, field2, field3String);
}
}
让您的不可变数据保持私有:
struct ImmutableStruct
{
private int field1;
private string field2;
private object field3;
public ImmutableStruct(int f1, string f2, object f3)
{
field1 = f1;
field2 = f2;
field3 = f3;
}
public int Field1 => field1;
public string Field2 => field2;
public object Field3 => field3;
}
或不太杂乱:
struct ImmutableStruct
{
public ImmutableStruct(int f1, string f2, object f3)
{
Field1 = f1;
Field2 = f2;
Field3 = f3;
}
public int Field1 { get; }
public string Field2 { get; }
public object Field3 { get; }
}
使字段
private只读
,并在构造函数中传递初始值
public struct ImmutableStruct
{
private readonly int _field1;
private readonly string _field2;
private readonly object _field3;
public ImmutableStruct(int f1, string f2, object f3)
{
_field1 = f1;
_field2 = f2;
_field3 = f3;
}
public int Field1 { get { return _field1; } }
public string Field2 { get { return _field2; } }
public object Field3 { get { return _field3; } }
}
从C#6.0(Visual Studio 2015)开始,您可以使用getter-only属性
public struct ImmutableStruct
{
public ImmutableStruct(int f1, string f2, object f3)
{
Field1 = f1;
Field2 = f2;
Field3 = f3;
}
public int Field1 { get; }
public string Field2 { get; }
public object Field3 { get; }
}
请注意,只读字段和仅getter属性可以在构造函数中初始化,也可以在类中使用字段或属性初始值设定项public int Field1{get;}=7代码>
不能保证构造函数在结构上运行。例如,如果您有一个结构数组,则必须为每个数组元素显式调用初始值设定项。对于引用类型的数组,所有元素首先初始化为null
,这使得您必须对每个元素调用new
。但是对于像structs这样的值类型,很容易忘记它
var immutables = new ImmutableStruct[10];
immutables[0] = new ImmutableStruct(5, "hello", new Person());
immutables[1] = new ImmutableStruct(6, "world", new Student());
...
从C#7.2开始,您可以使用
从C#9.0开始,还有另一种选择:使用。只读字段和仅获取自动实现的属性可以在构造函数和字段或属性初始值设定项中初始化,但不能在对象初始值设定项中初始化
这就是引入仅限init属性的动机。它们将set
访问器替换为init
访问器。这将突变阶段从实际对象创建扩展到整个对象构造阶段,包括对象初始值设定项和(也是一个新的C#9.0特性)
用法:
var x = new ImmutableStruct { Name = "John" };
x.Name = "Sue"; // Compiler error CS8852: Init-only property or indexer
// 'ImmutableStruct.Name' can only be assigned in an object initializer, or
// on 'this' or 'base' in an instance constructor or an 'init' accessor.
有点奇怪,但是有没有什么特别的原因让你需要一个结构而不是仅仅使用一个类?在多年的LOB编程中,我还没有发现结构优于类的用例。是否将字段标记为只读?另外,您看到了吗:心中的结构类似于C++的位域结构。结构是实例化的,我的愿望是结构永远不会被更改——我保证不会意外修改数据。Field3仍然被认为是可变的,因为它是引用类型。您必须在getter中返回对象的副本以保持实例不可变。或者使引用的对象本身不可变<代码>字符串
是引用类型,但不可变。由于结构表示值类型,所以对可变引用类型进行引用可能也不是一个好主意。回答得好!它的工作,是最新的!顺便说一下,如果您有一个属性初始值设定项,它只能出现在类中。否则您将收到错误:“不能在结构中使用实例属性或字段初始值设定项”
。好的,这是有意义的,因为初始化代码不能保证运行(例如在数组中)我认为你是否认为结构是不可变的,这取决于你是否只关心对象的引用相等性,或者你是否介意对象状态。您应该知道,结构总是返回相同的连接对象。
var x = new ImmutableStruct { Name = "John" };
x.Name = "Sue"; // Compiler error CS8852: Init-only property or indexer
// 'ImmutableStruct.Name' can only be assigned in an object initializer, or
// on 'this' or 'base' in an instance constructor or an 'init' accessor.