共用方式為


部分屬性

注意

本文是功能規格。 規格可作為功能的設計檔。 其中包含建議的規格變更,以及功能設計和開發期間所需的資訊。 這些文章會發佈,直到提議的規格變更完成並併併入目前的ECMA規格為止。

功能規格與已完成實作之間可能有一些差異。 這些差異是在 的相關語言設計會議(LDM)注意事項中擷取的。

您可以在 規格一文中深入瞭解將功能具體規範納入 C# 語言標準的過程

冠軍問題:https://github.com/dotnet/csharplang/issues/6420

語法

property_declaration 文法 (§14.7.1) 更新如下:

property_declaration
-    : attributes? property_modifier* type member_name property_body
+    : attributes? property_modifier* 'partial'? type member_name property_body
    ;  

備註:這與指定 method_header(§15.6.1)class_declaration(§15.2.1) 的方式有些類似。 (請注意,問題 #946 建議放寬排序需求,而且可能適用於所有允許 partial 修飾詞的宣告。我們打算在不遠的將來指定這樣的排序放寬,並在實作此功能的相同版本中實作它。

定義和實作宣告

當屬性宣告包含 部分 修飾詞時,該屬性稱為 部分屬性。 部分屬性只能宣告為部分類型的成員。

部分屬性 宣告據說是定義宣告 ,其存取子都有分號主體,而且缺少 extern 修飾詞。 否則,這是 的實作宣告

partial class C
{
    // Defining declaration
    public partial string Prop { get; set; }

    // Implementing declaration
    public partial string Prop { get => field; set => field = value; }
}

由於我們已針對定義宣告 保留分號存取子主體的語法形式,因此無法自動 自動實作部分屬性。 因此,我們調整 自動實作的屬性(§15.7.4),如下所示:

自動實作的屬性(或簡稱 auto 屬性)是非抽象、非 extern、非部分、 非 ref 值類型且存取子僅由分號組成的屬性。

備註。 編譯程式能夠隔離查看單一宣告,並知道它是定義或實作宣告,這非常有用。 因此,我們不想允許自動屬性,例如包含兩個相同的 partial 屬性宣告。 我們並不認為這項功能的使用案例牽涉到使用自動屬性實作部分屬性,但在想要簡單實作的情況下,我們認為 field 關鍵詞會讓事情變得足夠簡單。


部分屬性必須有一個 定義宣告,以及一個 實作宣告

備註。 我們也不認為允許將宣告分割到兩個以上的部分是有用的,從而允許在不同的位置實作不同的存取子。 因此,我們只是模仿部分方法所建立的方案。


只有部分屬性的定義宣告會參與查找,這與只有部分方法的定義宣告參與多載解析的方式相似。

備註。 在編譯程式中,我們預期只有定義宣告的符號會出現在成員清單中,而實作元件的符號可以透過定義符號來存取。 不過,某些功能,例如可為 Null 的分析,可能會 透過實作宣告 來查看,以提供更有用的行為。

partial class C
{
    public partial string Prop { get; set; }
    public partial string Prop { get => field; set => field = value; }

    public C() // warning CS8618: Non-nullable property 'Prop' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
    {
    }
}

不允許部分屬性具有 abstract 修飾詞。

部分屬性無法明確實作介面屬性。

屬性合併

與部分方法類似,結果屬性中的屬性是各部分屬性的組合,以未指定的順序串接,且不會移除重複項目。

呼叫端資訊屬性

我們將從 標準調整以下語言內容:

在定義和實作的部分上具有相同呼叫端資訊屬性的參數會導致一個錯誤,特別是在部分 方法成員 宣告中。 只會套用出現在定義部分的呼叫端資訊屬性,而只會忽略出現在實作部分的呼叫端資訊屬性。

  • 描述的錯誤不符合這些屬性的定義,因為這些屬性不包含 AllowMultiple = true。 使用它們多次,即使是在不同部分的宣告中使用,會導致錯誤。
  • 當呼叫端資訊屬性套用至部分方法實作部分的參數時,Roslyn 編譯程式會報告警告。 它也會在部分屬性中報告同樣情況的警告。

比對簽章

2020年9月14日 自民黨會議 定義了一組“嚴格”要求,用於簽章比對部分方法,這些要求是在警告波中引入的。 局部屬性對簽章比對的局部方法有儘可能類似的需求,不同之處在於所有的不匹配的診斷信息默認都會報告,而不是保留在警告波後面。

簽章比對需求包括:

  1. 部分屬性宣告中的類型和 ref 類型差異會對運行時結果產生影響,導致編譯階段錯誤。
  2. 部分屬性宣告內的 Tuple 元素名稱差異會導致編譯時期錯誤,這與部分方法的情況相同。
  3. 屬性宣告及其存取子宣告必須具有相同修飾詞,不過修飾詞可能會以不同的順序顯示。
    • 例外:這不適用於 extern 修飾詞,該修飾詞只會出現在 實作宣告中。
  4. 部分屬性宣告簽章中所有其他語法差異會導致編譯時警告,但有下列例外情況:
    • 屬性清單在部分屬性宣告的內部或上面不需要一致。 相反地,會根據 屬性合併,執行對應位置的屬性合併。
    • 可為 Null 的內容差異不會造成警告。 換句話說,其中一個類型忽略是否為 Null,而另一個類型則是附註為可為 Null 或不可為 Null 的狀態,不會產生任何警告。
    • 預設參數值不需要相符。 當部分索引器實作部分具有預設參數值時,就會報告警告。 這類似於現有的警告,當部分方法的實作部分具有預設參數值時,就會發生這個警告。
  5. 當參數名稱在定義宣告和實作宣告中不一致時,會發生警告。 定義部分的參數名稱會在使用位置和發出的時候使用。
  6. 不涉及模糊 null 性的 null 性差異會導致警告。 在分析存取器函數主體時,會使用實作部分的簽章。 當分析使用點和發出時,會使用定義部分簽章。 這與部分方法一致。
partial class C1
{
    public partial string Prop { get; private set; }

    // Error: accessor modifier mismatch in 'set' accessor of 'Prop'
    public partial string Prop { get => field; set => field = value; }
}

partial class C2
{
    public partial string Prop { get; init; }

    // Error: implementation of 'Prop' must have an 'init' accessor to match definition
    public partial string Prop { get => field; set => field = value; }
}

partial class C3
{
    public partial string Prop { get; }

    // Error: implementation of 'Prop' cannot have a 'set' accessor because the definition does not have a 'set' accessor.
    public partial string Prop { get => field; set => field = value; }
}

partial class C4
{
    public partial string this[string s = "a"] { get; set; }
    public partial string this[string s] { get => s; set { } } // ok

    public partial string this[int i, string s = "a"] { get; set; }
    public partial string this[int i, string s = "a"] { get => s; set { } } // CS1066: The default value specified for parameter 's' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments
}

文件註解

我們希望文件註解在部分屬性的行為與我們為部分方法推出的內容保持一致。 該行為會在 https://github.com/dotnet/csharplang/issues/5193中詳述。

允許在部分屬性的定義部分或實作部分包含文件註解。 請注意,屬性存取子不支援文件註釋。

當檔案批註僅存在於屬性的一部分時,這些批註將正常使用(例如透過 ISymbol.GetDocumentationCommentXml()顯示,或寫入至說明文件的 XML 檔案中)。

當這兩個部分都有檔批注時,定義元件上的所有檔批注都會卸除,而且只會使用實作元件上的檔批注。

例如,下列程式:

/// <summary>
/// My type
/// </summary>
partial class C
{
    /// <summary>Definition part comment</summary>
    /// <returns>Return value comment</returns>
    public partial int Prop { get; set; }
    
    /// <summary>Implementation part comment</summary>
    public partial int Prop { get => 1; set { } }
}

以下是 XML 文件檔案的內容結果:

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>ConsoleApp1</name>
    </assembly>
    <members>
        <member name="T:C">
            <summary>
            My type
            </summary>
        </member>
        <member name="P:C.Prop">
            <summary>
            Implementation part comment
            </summary>
        </member>
    </members>
</doc>

當部分宣告之間的參數名稱不同時,<paramref> 元素會使用原始程式碼中與檔批注相關聯的宣告的參數名稱。 例如,放置於實作宣告文件批註上的 paramref 將透過參數名稱來參考實作宣告中的參數符號。 這與部分方法一致。

/// <summary>
/// My type
/// </summary>
partial class C
{
    public partial int this[int x] { get; set; }

    /// <summary>
    /// <paramref name="x"/> // warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'x', but there is no parameter by that name
    /// <paramref name="y"/> // ok. 'Go To Definition' will go to 'int y'.
    /// </summary>
    public partial int this[int y] { get => 1; set { } } // warning CS9256: Partial property declarations 'int C.this[int x]' and 'int C.this[int y]' have signature differences.
}

以下是生成 XML 文件的結果:

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>ConsoleApp1</name>
    </assembly>
    <members>
        <member name="T:C">
            <summary>
            My type
            </summary>
        </member>
        <member name="P:C.Item(System.Int32)">
            <summary>
            <paramref name="x"/> // warning CS1734: XML comment on 'C.this[int]' has a paramref tag for 'x', but there is no parameter by that name
            <paramref name="y"/> // ok. 'Go To Definition' will go to 'int y'.
            </summary>
        </member>
    </members>
</doc>

這可能會造成混淆,因為元數據簽章會使用定義部分的參數名稱。 建議您確保參數名稱在各元件之間相符,以避免這種混淆。

索引器

根據 2022 年 11 月 2 日的 LDM 會議,此功能將為索引器提供支援。

索引器文法會修改如下:

indexer_declaration
-    : attributes? indexer_modifier* indexer_declarator indexer_body
+    : attributes? indexer_modifier* 'partial'? indexer_declarator indexer_body
-    | attributes? indexer_modifier* ref_kind indexer_declarator ref_indexer_body
+    | attributes? indexer_modifier* 'partial'? ref_kind indexer_declarator ref_indexer_body
    ;

部分索引器的參數必須遵循與 比對簽章中相同的規則宣告。 屬性合併 會針對部分索引器參數進行操作。

partial class C
{
    public partial int this[int x] { get; set; }
    public partial int this[int x]
    {
        get => this._store[x];
        set => this._store[x] = value;
    }
}

// attribute merging
partial class C
{
    public partial int this[[Attr1] int x]
    {
        [Attr2] get;
        set;
    }

    public partial int this[[Attr3] int x]
    {
        get => this._store[x];
        [Attr4] set => this._store[x] = value;
    }

    // results in a merged member emitted to metadata:
    public int this[[Attr1, Attr3] int x]
    {
        [Attr2] get => this._store[x];
        [Attr4] set => this._store[x] = value;
    }
}

未解決的問題

其他成員類型

社群成員開啟討論,要求支援 部分事件。 在2022年11月2日 自民黨會議上,我們決定對活動的支援,部分原因是當時沒有人要求。 我們可能想重新審視這個問題,因為這個要求現在進來了,而且自我們上次討論這個問題以來已經一年多了。

我們也可以在允許建構函式、運算元、欄位等的部分宣告方面更進一步,但目前還不清楚這些建構函式的設計負擔是否合理,只是因為我們已經在執行部分屬性。