Bagikan melalui


Properti parsial

Nota

Artikel ini adalah spesifikasi fitur. Spesifikasi berfungsi sebagai dokumen desain untuk fitur tersebut. Ini termasuk perubahan spesifikasi yang diusulkan, bersama dengan informasi yang diperlukan selama desain dan pengembangan fitur. Artikel ini diterbitkan sampai perubahan spesifikasi yang diusulkan diselesaikan dan dimasukkan dalam spesifikasi ECMA saat ini.

Mungkin ada beberapa perbedaan antara spesifikasi fitur dan implementasi yang selesai. Perbedaan tersebut dicatat dalam catatan rapat desain bahasa yang terkait.

Anda dapat mempelajari lebih lanjut tentang proses untuk mengadopsi speklet fitur ke dalam standar bahasa C# dalam artikel tentang spesifikasi .

Masalah juara: https://github.com/dotnet/csharplang/issues/6420

Tatabahasa

properti_deklarasi tata bahasa (§14.7.1) diperbarui sebagai berikut:

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

: Ini agak mirip dengan bagaimana method_header(§15.6.1) dan class_declaration(§15.2.1) ditentukan. (Perhatikan bahwa Masalah #946 mengusulkan untuk melonggarkan persyaratan urutan, dan mungkin akan berlaku untuk semua deklarasi yang memungkinkan pengubah partial. Kami bermaksud menentukan pelonggaran urutan seperti itu dalam waktu dekat, dan menerapkannya dalam rilis yang sama dengan penerapan fitur ini.)

Mendefinisikan dan menerapkan deklarasi

Ketika deklarasi properti menyertakan pengubah parsial, properti tersebut dikatakan sebagai properti parsial . Properti parsial hanya dapat dideklarasikan sebagai anggota jenis parsial.

Deklarasi properti parsial disebut sebagai deklarasi mendefinisikan ketika semua aksesornya memiliki badan berupa titik koma, dan tidak memiliki pengubah extern. Jika tidak, ini adalah deklarasi penerapan .

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

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

Karena kami telah mencadangkan formulir sindikat dengan badan aksesor titik koma untuk mendefinisikan deklarasi, properti parsial tidak dapat diimplementasikan secara otomatis. Oleh karena itu, kami menyesuaikan Properti yang diimplementasikan secara otomatis (§15.7.4) sebagai berikut:

Properti yang diimplementasikan secara otomatis (atau properti otomatis untuk singkatnya), adalah properti non-abstrak, non-ekstern, non-parsial, non-ref-valued dengan badan aksesor khusus titik koma.

Catatan. Hal ini berguna bagi kompilator untuk dapat melihat satu deklarasi dalam isolasi dan mengetahui apakah itu adalah deklarasi yang mendefinisikan atau menerapkan. Oleh karena itu, kami tidak ingin mengizinkan properti otomatis dengan menyertakan dua deklarasi properti partial yang identik, misalnya. Kami tidak berpikir bahwa kasus penggunaan untuk fitur ini melibatkan penerapan properti parsial dengan menggunakan properti otomatis, tetapi dalam kasus di mana diperlukan implementasi sederhana, kami pikir kata kunci field membuat semuanya cukup sederhana.


Properti parsial harus memiliki satu mendefinisikan deklarasi dan satu menerapkan deklarasi.

Catatan. Kami juga tidak berpikir bahwa berguna untuk mengizinkan pemisahan deklarasi menjadi lebih dari dua bagian, untuk memungkinkan aksesor yang berbeda diimplementasikan di lokasi yang berbeda, misalnya. Oleh karena itu kita hanya meniru skema yang ditetapkan dengan metode parsial.


Hanya deklarasi yang mendefinisikan properti parsial yang berpartisipasi dalam pencarian, mirip dengan bagaimana hanya deklarasi yang menentukan metode parsial yang berpartisipasi dalam resolusi kelebihan beban.

Catatan. Dalam kompilator, kita akan mengharapkan bahwa hanya simbol untuk deklarasi yang mendefinisikan yang muncul dalam daftar anggota, dan simbol untuk bagian pengimplementasian dapat diakses melalui simbol pendefinisi. Namun, beberapa fitur seperti analisis nullable mungkin melihat melalui ke deklarasi penerapan untuk memberikan perilaku yang lebih berguna.

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.
    {
    }
}

Properti parsial tidak diizinkan untuk memiliki pengubah abstract.

Properti parsial tidak dapat menerapkan properti antarmuka secara eksplisit.

Penggabungan atribut

Mirip dengan metode parsial, atribut dalam properti yang dihasilkan adalah atribut gabungan dari bagian yang digabungkan dalam urutan yang tidak ditentukan, dan duplikat tidak dihapus.

Atribut informasi penelepon

Kami menyesuaikan bahasa berikut dari standar :

Ini adalah kesalahan untuk memiliki atribut caller-info yang sama pada parameter dari kedua bagian yang mendefinisikan dan mengimplementasikan metode anggotadengan deklarasi parsial. Hanya atribut caller-info di bagian yang menentukan yang diterapkan, sedangkan atribut caller-info yang hanya terjadi di bagian penerapan diabaikan.

  • Kesalahan yang dijelaskan tidak termasuk dalam definisi atribut-atribut ini yang tidak memiliki AllowMultiple = true. Menggunakannya beberapa kali, termasuk dalam berbagai deklarasi parsial, menghasilkan kesalahan.
  • Ketika atribut caller-info diterapkan ke parameter di bagian implementasi dari metode parsial, kompilator Roslyn melaporkan peringatan. Ini juga akan melaporkan peringatan untuk skenario yang sama di properti parsial.

Pencocokan tanda tangan

Rapat LDM pada 14 September 2020 mendefinisikan serangkaian persyaratan "ketat" untuk pencocokan penandatanganan pada metode parsial, yang diperkenalkan sebagai bagian dari gelombang peringatan. Properti parsial memiliki persyaratan yang serupa dengan metode parsial untuk mencocokkan tanda tangan sejauh mungkin, kecuali bahwa semua diagnostik untuk ketidakcocokan dilaporkan secara default, dan tidak tersembunyi di belakang gelombang peringatan.

Persyaratan pencocokan tanda tangan meliputi:

  1. Perbedaan tipe dan jenis referensi antara deklarasi properti parsial yang signifikan terhadap hasil runtime mengakibatkan perbedaan tersebut menjadi kesalahan waktu kompilasi.
  2. Perbedaan nama elemen tuple dalam deklarasi properti parsial menghasilkan kesalahan waktu kompilasi, sama seperti untuk metode parsial.
  3. Deklarasi properti dan deklarasi aksesornya harus memiliki pengubah yang sama, meskipun pengubah mungkin muncul dalam urutan yang berbeda.
    • Pengecualian: ini tidak berlaku untuk pengubah extern, yang mungkin hanya muncul pada deklarasi implementasi .
  4. Semua perbedaan sintaks lainnya dalam rumusan deklarasi properti parsial menghasilkan peringatan waktu kompilasi, dengan pengecualian berikut:
    • Daftar atribut pada atau dalam deklarasi properti parsial tidak perlu cocok. Sebagai gantinya, penggabungan atribut dilakukan pada posisi yang sesuai, seperti yang dijelaskan dalam Penggabungan Atribut.
    • Perbedaan konteks yang dapat bernilai null tidak menyebabkan peringatan. Dengan kata lain, perbedaan di mana salah satu jenis nullable-oblivious dan jenis lainnya adalah nullable-annotated atau not-nullable-annotated tidak menghasilkan peringatan apa pun.
    • Nilai parameter default tidak perlu cocok. Peringatan dilaporkan ketika bagian implementasi dari pengindeks parsial memiliki nilai parameter default. Ini mirip dengan peringatan yang ada yang terjadi ketika bagian implementasi dari metode parsial memiliki nilai parameter default.
  5. Peringatan terjadi ketika nama parameter berbeda di seluruh deklarasi penentuan dan penerapan. Nama parameter dari bagian definisi digunakan di situs penggunaan dan dalam emisi.
  6. Perbedaan nullability yang tidak melibatkan nullability yang tidak diketahui mengakibatkan peringatan. Saat menganalisis isi aksesor, signatur bagian implementasi digunakan. Tanda tangan bagian definisi digunakan saat menganalisis situs penggunaan dan dalam memancarkan. Ini konsisten dengan metode parsial.
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
}

Komentar dokumentasi

Kami ingin perilaku komentar dokumen pada properti parsial konsisten dengan apa yang kami kirim untuk metode parsial. Perilaku itu dirinci dalam https://github.com/dotnet/csharplang/issues/5193.

Diizinkan untuk menyertakan komentar dokumen pada definisi atau bagian implementasi dari properti parsial. (Perhatikan bahwa komentar dokumen tidak didukung pada aksesor properti.)

Ketika komentar dokumen hanya ada di salah satu bagian properti, komentar dokumen tersebut digunakan secara normal (muncul melalui ISymbol.GetDocumentationCommentXml(), ditulis ke file XML dokumentasi, dll.).

Ketika komentar dokumen ada di kedua bagian, semua komentar dokumen pada bagian definisi dihilangkan, dan hanya komentar dokumen pada bagian implementasi yang digunakan.

Misalnya, program berikut:

/// <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 { } }
}

Hasil dalam file dokumentasi XML berikut:

<?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>

Ketika nama parameter berbeda antara deklarasi parsial, elemen <paramref> menggunakan nama parameter dari deklarasi yang terkait dengan komentar dokumentasi dalam kode sumber. Misalnya, paramref pada komentar dokumen yang ditempatkan pada deklarasi penerapan mengacu pada simbol parameter pada deklarasi penerapan menggunakan nama parameternya. Ini konsisten dengan metode parsial.

/// <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.
}

Hasil dalam file dokumentasi XML berikut:

<?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>

Ini bisa membingungkan, karena tanda tangan metadata akan menggunakan nama parameter dari bagian definisi. Disarankan untuk memastikan bahwa nama parameter cocok di seluruh bagian untuk menghindari kebingungan ini.

Pengindeks

Per rapat LDM pada 2 November 2022, pengindeks akan didukung dengan fitur ini.

Tata bahasa pengindeks dimodifikasi sebagai berikut:

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
    ;

Parameter pengindeksan parsial harus sesuai di seluruh deklarasi berdasarkan aturan yang sama seperti kecocokan tanda tangan . Penggabungan atribut dilakukan di seluruh parameter pengindeks parsial.

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

Masalah Terbuka

Jenis anggota lainnya

Anggota komunitas membuka diskusi guna meminta dukungan bagi sebagian acara . Dalam rapat LDM pada 2 November 2022, kami memutuskan untuk menunda dukungan untuk acara-acara, sebagian karena tidak ada yang memintanya pada saat itu. Kami mungkin ingin mengunjungi kembali pertanyaan ini, karena permintaan ini sekarang telah masuk, dan sudah lebih dari setahun sejak terakhir kali kami membahasnya.

Kita juga bisa melangkah lebih jauh dalam mengizinkan deklarasi parsial konstruktor, operator, bidang, dan sebagainya, tetapi tidak jelas apakah beban desain ini dibenarkan, hanya karena kita sudah melakukan properti parsial.