簡體   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