如何使结构不变? [英] How do I make a struct immutable?

查看:81
本文介绍了如何使结构不变?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在整个堆栈溢出和互联网上,我看到保持结构不变是一个很好的设计原则.不幸的是,我从未见过任何能真正实现这些结构真正不变的实现.

All over Stack Overflow and the internet I see that it is a good design principle to keep structs immutable. Unfortunately, I never see any implementation that actually causes these structs to be truly immutable.

假设结构内部没有任何引用类型,我实际上如何使结构不可变?也就是说,如何防止其任何原始字段发生突变(可能是由于编译时/运行时异常)?

Assuming that a struct does not have any reference types inside it, how do I actually make a struct immutable? That is, how do I prevent the mutation of any of its primitive field (perhaps by a compile-time/runtime exception)?

我编写了一个简单的测试,尝试使结构不可变,但甚至没有使用System.ComponentModel.ImmutableObjectAttribute起作用:

I wrote a simple test attempting make a struct immutable, but not even using the System.ComponentModel.ImmutableObjectAttribute worked:

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);
    }
}

推荐答案

使字段private readonly并在构造函数中传递初始值

Make the fields private readonly and pass the initial values in the constructor

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)开始,您可以使用仅吸气剂属性


Starting with C#6.0 (Visual Studio 2015) You can use getter only properties

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;初始化.

Note that readonly fields and getter only properties can be initialized either in the constructor or, in classes, also with field or property initializers public int Field1 { get; } = 7;.

不能保证构造函数在结构上运行.例如.如果您有一个结构体数组,则必须为每个数组元素显式调用初始化程序.对于引用类型的数组,首先将所有元素初始化为null,这很明显,您必须在每个元素上调用new.但是对于诸如结构之类的值类型,很容易忘记它.

It is not guaranteed that the constructor is run on a struct. E.g. if you have an array of structs, you must then call the initializers explicitly for each array element. For arrays of reference types all the elements are first initialized to null, which makes it obvious that you have to call new on each element. But it is easy to forget it for value types like 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开始,还有另一种选择:仅初始化属性.只读字段和仅自动实现的属性可以在构造函数,字段或属性初始化程序中初始化,而不能在对象初始化程序中初始化.

Starting with C# 9.0 there is yet another option: the Init-Only Properties. Read-only fields and get-only auto implemented properties can be initialized in a constructor and in the field or property initializer but not in an object initializer.

这是引入仅初始化属性的动机.它们将set访问器替换为init访问器.这将突变阶段从实际的对象创建扩展到整个对象构造阶段,包括对象初始化程序和

This is the motivation for introducing init-only properties. They replace the set accessor by an init accessor. This extends the mutation phase from the actual object creation to the entire object construction phase including object initializers and with expressions (also a new C# 9.0 feature).

public string Name { get; init; }

用法:

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.

这篇关于如何使结构不变?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆