繁体   English   中英

对象初始值设定项中的只读字段

[英]Readonly field in object initializer

我想知道为什么不能执行以下操作:

struct TestStruct
{
    public readonly object TestField;
}

TestStruct ts = new TestStruct {
    /* TestField = "something" // Impossible */
};

对象初始值设定项不应该能够设置字段的值吗?

Object Initializer 在内部使用一个临时对象,然后将每个值分配给属性。 拥有只读字段会破坏这一点。

下列的

TestStruct ts = new TestStruct 
{
     TestField = "something";
};

会翻译成

TestStruct ts;
var tmp = new TestStruct();
tmp.TestField = "something"; //this is not possible
ts = tmp;

(这是Jon Skeet回答,解释了临时对象与对象初始化器的用法,但使用了不同的场景)

readonly表示该字段只能在构造函数(或字段初始值设定项)中设置。 在构造函数返回设置对象初始值设定项中指定的属性。 那是,

TestStruct ts = new TestStruct {
    TestField = "something"
};

基本上相当于

TestStruct ts = new TestStruct();
ts.TestField = "something";

(在 Debug 版本中,编译器可能会使用一个临时变量,但您明白了。)

C# 9 Init-Only Properties尽管名称如此,但也允许初始化程序语法能够设置只读字段

以下是从链接中复制的相关部分。

仅初始化属性

这是对象初始化程序的一个简单示例。

new Person
{
    FirstName = "Scott",
    LastName = "Hunter"
}

今天的一大限制是属性必须是可变的,对象初始值设定项才能工作:它们通过首先调用对象的构造函数(在本例中为默认的无参数构造函数)然后分配给属性设置器来起作用。

仅初始化属性解决了这个问题! 他们引入了一个init访问器,它是set访问器的变体,只能在对象初始化期间调用:

public class Person
{
    public string FirstName { get; init; }
    public string LastName { get; init; }
}

有了这个声明,上面的客户端代码仍然是合法的,但是对FirstNameLastName属性的任何后续分配都是错误的。

初始化访问器和只读字段

因为init访问器只能在初始化期间被调用,所以它们可以改变封闭类的readonly字段,就像在构造函数中一样。

public class Person
{
    private readonly string firstName;
    private readonly string lastName;
    
    public string FirstName 
    { 
        get => firstName; 
        init => firstName = (value ?? throw new ArgumentNullException(nameof(FirstName)));
    }
    public string LastName 
    { 
        get => lastName; 
        init => lastName = (value ?? throw new ArgumentNullException(nameof(LastName)));
    }
}

这不可能。 因为readonly字段不能从ConstructorField Initializer以外的地方分配。

你展示的实际上是object initializer 它只是一个语法糖,被编译成这样的东西

TestStruct ts;
TestStruct ts1 = new TestStruct();
ts1.TestField = value;
ts = ts1;

这清楚为什么它不编译吗?

我想知道为什么不能执行以下操作:

因为编译器无法确定会执行以下代码:

TestStruct ts = new TestStruct 
{
    TestField = "something"
};

您应该直接内联或在构造函数内部初始化只读成员。

MSDN

readonly关键字是可以在字段上使用的修饰符。 当字段声明包含 readonly 修饰符时,对声明引入的字段的赋值只能作为声明的一部分或在同一类的构造函数中发生

所以它根本(还)不可能,因为对象初始值设定项只是创建后的分配。

因为对象初始值设定项只是一种简短的初始化方式:

TestStruct ts = new TestStruct {
  TestField = "something";
};

是相同的(编译器会将上面的内容翻译成这个):

TestStruct ts = new TestStruct();
ts.TestField = "something";//this is of course not allowed.

在 readonly 字段扩展 CollectionBase 的情况下,我遇到了一个有趣的“异常”。

这是代码:

using System.Collections;

namespace ReadOnly
{
    class Program
    {
        static void Main(string[] args)
        {
            Foo foo1 = new Foo()
            {
                Bar = new Bar()  // Compile error here - readonly property.
                {
                    new Buzz() { Name = "First Buzz" }
                }
            };

            Foo foo2 = new Foo()
            {
                Bar = // No Compile error here.
                {
                    new Buzz { Name = "Second Buzz" }
                }
            };
        }
    }

    class Foo
    {
        public Bar Bar { get; }
    }

    class Bar : CollectionBase
    {
        public int Add(Buzz value)
        {
            return List.Add(value);
        }

        public Buzz this[int index]
        {
            get { return (Buzz)List[index]; }
            set { List[index] = value; }
        }
    }

    class Buzz
    {
        public string Name { get; set; }
    }
}

Foo1 是我最初尝试这样做的方式(所有这些类都来自外部库,所以我们一开始不知道 Bar 是只读的)。 得到编译错误。 然后不小心我像 foo2 一样重新输入它,它起作用了。

在反编译 dll 并看到 Bar 扩展了 CollectionBase 之后,我们意识到第二种语法(foo2)正在调用集合上的 Add 方法。 因此,在集合的情况下,虽然您不能设置只读属性,但您可以通过对象初始值设定项调用 Add 方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM