Bagikan melalui


22 Atribut

22.1 Umum

Sebagian besar bahasa C# memungkinkan programmer menentukan informasi deklaratif tentang entitas yang ditentukan dalam program. Misalnya, aksesibilitas metode di kelas ditentukan dengan mendekorasinya dengan method_modifier, public, protectedinternal, dan private.

C# memungkinkan programmer untuk menciptakan jenis informasi deklaratif baru, yang disebut atribut. Programmer kemudian dapat melampirkan atribut ke berbagai entitas program, dan mengambil informasi atribut di lingkungan run-time.

Catatan: Misalnya, kerangka kerja mungkin menentukan HelpAttribute atribut yang dapat ditempatkan pada elemen program tertentu (seperti kelas dan metode) untuk menyediakan pemetaan dari elemen program tersebut ke dokumentasi mereka. catatan akhir

Atribut didefinisikan melalui deklarasi kelas atribut (§22.2), yang dapat memiliki parameter posisi dan nama (§22.2.3). Atribut dilampirkan ke entitas dalam program C# menggunakan spesifikasi atribut (§22.3), dan dapat diambil pada run-time sebagai instans atribut (§22.4).

22.2 Kelas atribut

22.2.1 Umum

Kelas yang berasal dari kelas System.Attributeabstrak , baik secara langsung maupun tidak langsung, adalah kelas atribut. Deklarasi kelas atribut mendefinisikan jenis atribut baru yang dapat ditempatkan pada entitas program. Menurut konvensi, kelas atribut dinamai dengan akhiran Attribute. Penggunaan atribut dapat mencakup atau menghilangkan akhiran ini.

Deklarasi kelas generik tidak boleh digunakan System.Attribute sebagai kelas dasar langsung atau tidak langsung.

Contoh:

public class B : Attribute {}
public class C<T> : B {} // Error – generic cannot be an attribute

contoh akhir

22.2.2 Penggunaan atribut

Atribut AttributeUsage (§22.5.2) digunakan untuk menjelaskan bagaimana kelas atribut dapat digunakan.

AttributeUsage memiliki parameter posisi (§22.2.3) yang memungkinkan kelas atribut untuk menentukan jenis entitas program tempatnya dapat digunakan.

Contoh: Contoh berikut mendefinisikan kelas atribut bernama SimpleAttribute yang dapat ditempatkan pada class_declarationdan interface_declarations saja, dan menunjukkan beberapa penggunaan Simple atribut.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface)]
public class SimpleAttribute : Attribute
{ 
    ... 
}

[Simple] class Class1 {...}
[Simple] interface Interface1 {...}

Meskipun atribut ini didefinisikan dengan nama SimpleAttribute, ketika atribut ini digunakan, Attribute akhiran dapat dihilangkan, menghasilkan nama Simplependek . Dengan demikian, contoh di atas secara semantik setara dengan yang berikut ini

[SimpleAttribute] class Class1 {...}
[SimpleAttribute] interface Interface1 {...}

contoh akhir

AttributeUsage memiliki parameter bernama (§22.2.3), yang disebut AllowMultiple, yang menunjukkan apakah atribut dapat ditentukan lebih dari sekali untuk entitas tertentu. Jika AllowMultiple untuk kelas atribut benar, kelas atribut tersebut adalah kelas atribut multi-penggunaan, dan dapat ditentukan lebih dari sekali pada entitas. Jika AllowMultiple untuk kelas atribut salah atau tidak ditentukan, kelas atribut tersebut adalah kelas atribut sekali pakai, dan dapat ditentukan paling banyak sekali pada entitas.

Contoh: Contoh berikut mendefinisikan kelas atribut multi-penggunaan bernama AuthorAttribute dan menunjukkan deklarasi kelas dengan dua penggunaan Author atribut:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public AuthorAttribute(string name) => Name = name;
}

[Author("Brian Kernighan"), Author("Dennis Ritchie")]
class Class1 
{
    ...
}

contoh akhir

AttributeUsage memiliki parameter bernama lain (§22.2.3), yang disebut Inherited, yang menunjukkan apakah atribut, ketika ditentukan pada kelas dasar, juga diwarisi oleh kelas yang berasal dari kelas dasar tersebut. Jika Inherited untuk kelas atribut benar, maka atribut tersebut diwariskan. Jika Inherited untuk kelas atribut salah, atribut tersebut tidak diwariskan. Jika tidak ditentukan, nilai defaultnya adalah true.

Kelas X atribut yang tidak memiliki atribut yang AttributeUsage melekat padanya, seperti dalam

class X : Attribute { ... }

setara dengan yang berikut ini:

[AttributeUsage(
   AttributeTargets.All,
   AllowMultiple = false,
   Inherited = true)
]
class X : Attribute { ... }

22.2.3 Parameter posisi dan bernama

Kelas atribut dapat memiliki parameterposisi dan parameterbernama s. Setiap konstruktor instans publik untuk kelas atribut menentukan urutan parameter posisi yang valid untuk kelas atribut tersebut. Setiap bidang baca-tulis publik non-statis dan properti untuk kelas atribut menentukan parameter bernama untuk kelas atribut. Agar properti menentukan parameter bernama, properti tersebut harus memiliki aksesor get publik dan aksesor set publik.

Contoh: Contoh berikut mendefinisikan kelas atribut bernama HelpAttribute yang memiliki satu parameter posisi, url, dan satu parameter bernama, Topic. Meskipun tidak statis dan publik, properti Url tidak menentukan parameter bernama, karena tidak baca-tulis. Dua penggunaan atribut ini juga ditampilkan:

[AttributeUsage(AttributeTargets.Class)]
public class HelpAttribute : Attribute
{
    public HelpAttribute(string url) // url is a positional parameter
    { 
        ...
    }

    // Topic is a named parameter
    public string Topic
    { 
        get;
        set;
    }

    public string Url { get; }
}

[Help("http://www.mycompany.com/xxx/Class1.htm")]
class Class1
{
}

[Help("http://www.mycompany.com/xxx/Misc.htm", Topic ="Class2")]
class Class2
{
}

contoh akhir

22.2.4 Jenis parameter atribut

Jenis parameter posisi dan bernama untuk kelas atribut terbatas pada jenis parameter atribut, yaitu:

  • Salah satu jenis berikut: bool, , byte, char, double, float, intlong, sbyte, , short, string, uint, ulong, ushort.
  • Jenis object.
  • Jenis System.Type.
  • Jenis enum.
  • Array dimensi tunggal dari jenis di atas.
  • Argumen konstruktor atau bidang publik yang tidak memiliki salah satu jenis ini, tidak boleh digunakan sebagai parameter posisi atau bernama dalam spesifikasi atribut.

22.3 Spesifikasi atribut

Spesifikasi atribut adalah aplikasi atribut yang ditentukan sebelumnya ke entitas program. Atribut adalah bagian dari informasi deklaratif tambahan yang ditentukan untuk entitas program. Atribut dapat ditentukan pada cakupan global (untuk menentukan atribut pada rakitan atau modul yang berisi) dan untuk type_declaration (§14,7), class_member_declarations (§15,3), interface_member_declarations (18,4), struct_member_declarations (§16,3), enum_member_declarations (§19,2), accessor_declarations (§15,7,3), event_accessor_ deklarasi(§15,8), elemen parameter_lists (§15,6,2), dan elemen type_parameter_lists (§15.2.3).

Atribut ditentukan dalam bagian atribut. Bagian atribut terdiri dari sepasang kurung siku, yang mengelilingi daftar yang dipisahkan koma dari satu atau beberapa atribut. Urutan di mana atribut ditentukan dalam daftar seperti itu, dan urutan di mana bagian yang dilampirkan ke entitas program yang sama diatur, tidak signifikan. Misalnya, spesifikasi [A][B]atribut , , [B][A][A, B], dan [B, A] setara.

global_attributes
    : global_attribute_section+
    ;

global_attribute_section
    : '[' global_attribute_target_specifier attribute_list ']'
    ;

global_attribute_target_specifier
    : global_attribute_target ':'
    ;

global_attribute_target
    : identifier
    ;

attributes
    : attribute_section+
    ;

attribute_section
    : '[' attribute_target_specifier? attribute_list ']'
    ;

attribute_target_specifier
    : attribute_target ':'
    ;

attribute_target
    : identifier
    | keyword
    ;

attribute_list
    : attribute (',' attribute)* ','?
    ;

attribute
    : attribute_name attribute_arguments?
    ;

attribute_name
    : type_name
    ;

attribute_arguments
    : '(' ')'
    | '(' positional_argument_list (',' named_argument_list)? ')'
    | '(' named_argument_list ')'
    ;

positional_argument_list
    : positional_argument (',' positional_argument)*
    ;

positional_argument
    : argument_name? attribute_argument_expression
    ;

named_argument_list
    : named_argument (','  named_argument)*
    ;

named_argument
    : identifier '=' attribute_argument_expression
    ;

attribute_argument_expression
    : non_assignment_expression
    ;

Untuk global_attribute_target produksi, dan dalam teks di bawah ini, pengidentifikasi harus memiliki ejaan yang sama dengan atau , di mana kesetaraan assembly yang ditentukan dalam module. Untuk attribute_target produksi, dan dalam teks di bawah ini, pengidentifikasi harus memiliki ejaan yang tidak sama dengan assembly atau module, menggunakan definisi kesetaraan yang sama seperti di atas.

Atribut terdiri dari attribute_name dan daftar opsional argumen posisi dan bernama. Argumen posisi (jika ada) mendahului argumen bernama. Argumen posisi terdiri dari attribute_argument_expression; argumen bernama terdiri dari nama, diikuti dengan tanda sama dengan, diikuti oleh attribute_argument_expression, yang, bersama-sama, dibatasi oleh aturan yang sama dengan penugasan sederhana. Urutan argumen bernama tidak signifikan.

Catatan: Untuk kenyamanan, koma berikutnya diizinkan dalam global_attribute_section dan attribute_section, sama seperti yang diizinkan dalam array_initializer (§17,7). catatan akhir

attribute_name mengidentifikasi kelas atribut.

Saat atribut ditempatkan di tingkat global, global_attribute_target_specifier diperlukan. Saat global_attribute_target sama dengan:

  • assembly — targetnya adalah rakitan yang berisi
  • module — targetnya adalah modul yang berisi

Tidak ada nilai lain untuk global_attribute_target yang diizinkan.

Nama attribute_target yang distandarkan adalah , , , event, fieldmethod, param, property, dan return.typetypevar Nama target ini hanya akan digunakan dalam konteks berikut:

  • event — sebuah peristiwa.
  • field — bidang. Peristiwa seperti bidang (yaitu, satu tanpa pengakses) (§15.8.2) dan properti yang diimplementasikan secara otomatis (§15.7.4) juga dapat memiliki atribut dengan target ini.
  • method — konstruktor, finalizer, metode, operator, properti mendapatkan dan mengatur aksesor, pengindeks mendapatkan dan mengatur aksesor, dan peristiwa menambahkan dan menghapus aksesor. Peristiwa seperti bidang (yaitu, satu tanpa aksesor) juga dapat memiliki atribut dengan target ini.
  • param — pengakses set properti, pengakses set pengindeks, pengakses penambahan dan penghapusan peristiwa, dan parameter dalam konstruktor, metode, dan operator.
  • property — properti dan pengindeks.
  • return — delegasi, metode, operator, properti dapatkan aksesor, dan pengindeks mendapatkan aksesor.
  • type — delegasi, kelas, struktur, enum, dan antarmuka.
  • typevar — parameter jenis.

Konteks tertentu mengizinkan spesifikasi atribut pada lebih dari satu target. Program dapat secara eksplisit menentukan target dengan menyertakan attribute_target_specifier. Tanpa attribute_target_specifier default diterapkan, tetapi attribute_target_specifier dapat digunakan untuk mengafirmasi atau mengambil alih default. Konteks diselesaikan sebagai berikut:

  • Untuk atribut pada deklarasi delegasi, target default adalah delegasi. Jika tidak, jika attribute_target sama dengan:
    • type — target adalah delegasi
    • return — target adalah nilai pengembalian
  • Untuk atribut pada deklarasi metode, target default adalah metode . Jika tidak, jika attribute_target sama dengan:
    • method — targetnya adalah metode
    • return — target adalah nilai pengembalian
  • Untuk atribut pada deklarasi operator, target default adalah operator. Jika tidak, jika attribute_target sama dengan:
    • method — targetnya adalah operator
    • return — target adalah nilai pengembalian
  • Untuk atribut pada deklarasi dapatkan aksesor untuk deklarasi properti atau pengindeks, target default adalah metode terkait. Jika tidak, jika attribute_target sama dengan:
    • method — target adalah metode terkait
    • return — target adalah nilai pengembalian
  • Untuk atribut yang ditentukan pada aksesor yang ditetapkan untuk deklarasi properti atau pengindeks, target default adalah metode terkait. Jika tidak, jika attribute_target sama dengan:
    • method — target adalah metode terkait
    • param — target adalah parameter implisit kesepian
  • Untuk atribut pada deklarasi properti yang diimplementasikan secara otomatis, target default adalah properti . Jika tidak, jika attribute_target sama dengan:
    • field — target adalah bidang backing yang dihasilkan kompilator untuk properti
  • Untuk atribut yang ditentukan pada deklarasi peristiwa yang menghilangkan event_accessor_declarations target default adalah deklarasi peristiwa. Jika tidak, jika attribute_target sama dengan:
    • event — targetnya adalah deklarasi peristiwa
    • field — target adalah bidang
    • method — targetnya adalah metode
  • Dalam kasus deklarasi peristiwa yang tidak menghilangkan event_accessor_declarations target default adalah metode .
    • method — target adalah metode terkait
    • param — targetnya adalah parameter lone

Dalam semua konteks lainnya, penyertaan attribute_target_specifier diizinkan tetapi tidak perlu.

Contoh: deklarasi kelas dapat mencakup atau menghilangkan penentu type:

[type: Author("Brian Kernighan")]
class Class1 {}

[Author("Dennis Ritchie")]
class Class2 {}

contoh akhir.

Implementasi dapat menerima attribute_targetlain, tujuan implementasi yang ditentukan. Implementasi yang tidak mengenali attribute_target tersebut akan mengeluarkan peringatan dan mengabaikan attribute_section yang berisi.

Menurut konvensi, kelas atribut dinamai dengan akhiran Attribute. Attribute_name dapat menyertakan atau menghilangkan akhiran ini. Secara khusus, attribute_name diselesaikan sebagai berikut:

  • Jika pengidentifikasi paling kanan dari attribute_name adalah pengidentifikasi verbatim (§6.4.3), maka attribute_name diselesaikan sebagai type_name (§7,8). Jika hasilnya bukan jenis yang berasal dari System.Attribute, kesalahan waktu kompilasi terjadi.
  • Sebaliknya
    • attribute_name diselesaikan sebagai type_name (§7,8) kecuali kesalahan apa pun ditekan. Jika resolusi ini berhasil dan menghasilkan jenis yang berasal dari System.Attribute maka jenisnya adalah hasil dari langkah ini.
    • Karakter Attribute ditambahkan ke pengidentifikasi paling kanan dalam attribute_name dan string token yang dihasilkan diselesaikan sebagai type_name (§7,8) kecuali kesalahan apa pun ditekan. Jika resolusi ini berhasil dan menghasilkan jenis yang berasal dari System.Attribute maka jenisnya adalah hasil dari langkah ini.

Jika tepat salah satu dari dua langkah di atas menghasilkan jenis yang berasal dari System.Attribute, maka jenis tersebut adalah hasil dari attribute_name. Jika tidak, kesalahan waktu kompilasi terjadi.

Contoh: Jika kelas atribut ditemukan dengan dan tanpa akhiran ini, ambiguitas ada, dan hasil kesalahan waktu kompilasi. Jika attribute_name dieja sedih sehingga pengidentifikasi paling kanan adalah pengidentifikasi verbatim (§6.4.3), maka hanya atribut tanpa akhiran yang cocok, sehingga memungkinkan ambiguitas seperti itu diselesaikan. Contoh

[AttributeUsage(AttributeTargets.All)]
public class Example : Attribute
{}

[AttributeUsage(AttributeTargets.All)]
public class ExampleAttribute : Attribute
{}

[Example]               // Error: ambiguity
class Class1 {}

[ExampleAttribute]      // Refers to ExampleAttribute
class Class2 {}

[@Example]              // Refers to Example
class Class3 {}

[@ExampleAttribute]     // Refers to ExampleAttribute
class Class4 {}

menunjukkan dua kelas atribut bernama Example dan ExampleAttribute. Atributnya [Example] ambigu, karena dapat merujuk ke Example atau ExampleAttribute. Menggunakan pengidentifikasi verbatim memungkinkan niat yang tepat untuk ditentukan dalam kasus langka seperti itu. Atribut [ExampleAttribute] tidak ambigu (meskipun akan jika ada kelas atribut bernama ExampleAttributeAttribute!). Jika deklarasi untuk kelas Example dihapus, kedua atribut merujuk ke kelas atribut bernama ExampleAttribute, sebagai berikut:

[AttributeUsage(AttributeTargets.All)]
public class ExampleAttribute : Attribute
{}

[Example]            // Refers to ExampleAttribute
class Class1 {}

[ExampleAttribute]   // Refers to ExampleAttribute
class Class2 {}

[@Example]           // Error: no attribute named “Example”
class Class3 {}

contoh akhir

Ini adalah kesalahan waktu kompilasi untuk menggunakan kelas atribut sekali pakai lebih dari sekali pada entitas yang sama.

Contoh: Contoh

[AttributeUsage(AttributeTargets.Class)]
public class HelpStringAttribute : Attribute
{
    public HelpStringAttribute(string value)
    {
        Value = value;
    }

    public string Value { get; }
}
[HelpString("Description of Class1")]
[HelpString("Another description of Class1")]   // multiple uses not allowed
public class Class1 {}

menghasilkan kesalahan waktu kompilasi HelpStringkarena mencoba menggunakan Class1, yang merupakan kelas atribut sekali pakai, lebih dari sekali pada deklarasi .

contoh akhir

Ekspresi E adalah attribute_argument_expression jika semua pernyataan berikut ini benar:

  • Jenisnya E adalah jenis parameter atribut (§22.2.4).
  • Pada waktu kompilasi E , nilai dapat diatasi ke salah satu hal berikut:
    • Nilai konstanta.
    • Objek System.Type yang diperoleh menggunakan typeof_expression (§12.8.18) yang menentukan jenis non-generik, jenis konstruksi tertutup (§8.4.3), atau jenis generik yang tidak terikat (§8.4.4), tetapi bukan jenis terbuka (§8.4.3).
    • Array dimensi tunggal attribute_argument_expression.

Contoh:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field)]
public class TestAttribute : Attribute
{
    public int P1 { get; set; }

    public Type P2 { get; set; }

    public object P3 { get; set; }
}

[Test(P1 = 1234, P3 = new int[]{1, 3, 5}, P2 = typeof(float))]
class MyClass {}

class C<T> {
    [Test(P2 = typeof(T))] // Error – T not a closed type.
    int x1;

    [Test(P2 = typeof(C<T>))] // Error – C<;T>; not a closed type.
    int x2;

    [Test(P2 = typeof(C<int>))] // Ok
    int x3;

    [Test(P2 = typeof(C<>))] // Ok
    int x4;
}

contoh akhir

Atribut jenis yang dideklarasikan dalam beberapa bagian ditentukan dengan menggabungkan, dalam urutan yang tidak ditentukan, atribut masing-masing bagiannya. Jika atribut yang sama ditempatkan pada beberapa bagian, atribut tersebut setara dengan menentukan atribut tersebut beberapa kali pada jenisnya.

Contoh: Dua bagian:

[Attr1, Attr2("hello")]
partial class A {}

[Attr3, Attr2("goodbye")]
partial class A {}

setara dengan deklarasi tunggal berikut:

[Attr1, Attr2("hello"), Attr3, Attr2("goodbye")]
class A {}

contoh akhir

Atribut pada parameter jenis digabungkan dengan cara yang sama.

22.4 Instans atribut

22.4.1 Umum

Instans atribut adalah instans yang mewakili atribut pada run-time. Atribut didefinisikan dengan kelas atribut, argumen posisi, dan argumen bernama. Instans atribut adalah instans kelas atribut yang diinisialisasi dengan argumen posisi dan bernama.

Pengambilan instans atribut melibatkan pemrosesan waktu kompilasi dan run-time, seperti yang dijelaskan dalam subklasul berikut.

22.4.2 Kompilasi atribut

Kompilasi atribut dengan kelas Tatribut , positional_argument_listP, , dan ditentukan pada entitas N program dikompilasi ke dalam rakitan E melalui langkah-langkah berikut:

  • Ikuti langkah-langkah pemrosesan waktu kompilasi untuk mengkompilasi object_creation_expression formulir baru T(P). Langkah-langkah ini mengakibatkan kesalahan waktu kompilasi, atau menentukan konstruktor C instans pada T yang dapat dipanggil pada run-time.
  • Jika C tidak memiliki aksesibilitas publik, maka terjadi kesalahan waktu kompilasi.
  • Untuk setiap named_argumentArg di N:
    • Biarkan Name menjadi pengidentifikasi
    • Name harus mengidentifikasi bidang publik baca-tulis non-statis atau properti pada T. Jika T tidak memiliki bidang atau properti seperti itu, maka terjadi kesalahan waktu kompilasi.
  • Jika salah satu nilai dalam positional_argument_listP atau salah satu nilai dalam named_argument_listN berjenis System.String dan nilainya tidak terbentuk dengan baik seperti yang didefinisikan oleh Standar Unicode, nilai yang didefinisikan implementasinya sama dengan nilai run-time yang diambil (§22.4.3).

    Catatan: Sebagai contoh, string yang berisi unit kode UTF-16 pengganti tinggi yang tidak segera diikuti oleh unit kode pengganti rendah tidak terbentuk dengan baik. catatan akhir

  • Simpan informasi berikut (untuk instansiasi run-time atribut) dalam output perakitan oleh pengkompilasi sebagai hasil dari mengkompilasi program yang berisi atribut: kelas Tatribut , konstruktor C instans pada T, , P, dan entitas Nprogram terkait , dengan nilai diselesaikan sepenuhnya pada waktu kompilasi.

22.4.3 Pengambilan run-time instans atribut

Menggunakan istilah yang ditentukan dalam §22.4.2, instans atribut yang diwakili oleh T, , CP, dan N, dan yang terkait dengan E dapat diambil pada run-time dari perakitan A menggunakan langkah-langkah berikut:

  • Ikuti langkah-langkah pemrosesan run-time untuk menjalankan object_creation_expression formulir new T(P), menggunakan konstruktor C instans dan nilai seperti yang ditentukan pada waktu kompilasi. Langkah-langkah ini menghasilkan pengecualian, atau menghasilkan instans OT.
  • Untuk setiap named_argumentArg dalam N, secara berurutan:
    • Biarkan Name menjadi pengidentifikasi Jika Name tidak mengidentifikasi bidang baca-tulis publik atau properti non-statis pada O, maka pengecualian akan dilemparkan.
    • Mari kita Value jadikan hasil mengevaluasi attribute_argument_expression .Arg
    • Jika Name mengidentifikasi bidang pada O, maka atur bidang ini ke Value.
    • Jika tidak, Nama mengidentifikasi properti pada O. Atur properti ini ke Nilai.
    • Hasilnya adalah O, instans kelas T atribut yang telah diinisialisasi dengan positional_argument_listP dan named_argument_listN.

Catatan: Format untuk menyimpan T, , C, PN(dan mengaitkannya dengan E) di A dan mekanisme untuk menentukan E dan mengambil T, , CP, N dari A (dan karenanya bagaimana instans atribut diperoleh pada runtime) berada di luar cakupan spesifikasi ini. catatan akhir

Contoh: Dalam implementasi CLI, Help instans atribut dalam rakitan yang dibuat dengan mengkompilasi program contoh di §22.2.3 dapat diambil dengan program berikut:

public sealed class InterrogateHelpUrls
{
    public static void Main(string[] args)
    {
        Type helpType = typeof(HelpAttribute);
        string assemblyName = args[0];
        foreach (Type t in Assembly.Load(assemblyName).GetTypes()) 
        {
            Console.WriteLine($"Type : {t}");
            var attributes = t.GetCustomAttributes(helpType, false);
            var helpers = (HelpAttribute[]) attributes;
            foreach (var helper in helpers)
            {
                Console.WriteLine($"\tUrl : {helper.Url}");
            }
        }
    }
}

contoh akhir

22.5 Atribut cadangan

22.5.1 Umum

Sejumlah atribut memengaruhi bahasa dalam beberapa cara. Atribut ini meliputi:

  • System.AttributeUsageAttribute (§22.5.2), yang digunakan untuk menggambarkan cara kelas atribut dapat digunakan.
  • System.Diagnostics.ConditionalAttribute (§22.5.3), adalah kelas atribut multi-penggunaan yang digunakan untuk menentukan metode kondisional dan kelas atribut kondisional. Atribut ini menunjukkan kondisi dengan menguji simbol kompilasi bersyarah.
  • System.ObsoleteAttribute (§22.5.4), yang digunakan untuk menandai anggota sebagai usang.
  • System.Runtime.CompilerServices.AsyncMethodBuilderAttribute (§22.5.5), yang digunakan untuk membuat pembangun tugas untuk metode asinkron.
  • System.Runtime.CompilerServices.CallerLineNumberAttribute (§22.5.6.2), System.Runtime.CompilerServices.CallerFilePathAttribute (§22.5.6.3), dan System.Runtime.CompilerServices.CallerMemberNameAttribute (§22.5.6.4), yang digunakan untuk memberikan informasi tentang konteks panggilan ke parameter opsional.

Atribut analisis statis nullable (§22.5.7) dapat meningkatkan kebenaran peringatan yang dihasilkan untuk nullabilities dan status null (§8.9.5).

Lingkungan eksekusi dapat menyediakan atribut tambahan yang ditentukan implementasi yang memengaruhi eksekusi program C#.

22.5.2 AtributUsage

Atribut AttributeUsage digunakan untuk menjelaskan cara kelas atribut dapat digunakan.

Kelas yang didekorasi dengan AttributeUsage atribut harus berasal dari System.Attribute, baik secara langsung maupun tidak langsung. Jika tidak, terjadi kesalahan waktu kompilasi.

Catatan: Untuk contoh penggunaan atribut ini, lihat §22.2.2. catatan akhir

22.5.3 Atribut Bersyarah

22.5.3.1 Umum

Atribut Conditional memungkinkan definisi metode kondisional dan kelas atribut kondisional.

22.5.3.2 Metode bersyarah

Metode yang didekorasi dengan Conditional atribut adalah metode bersyarkat. Setiap metode bersyarat dengan demikian dikaitkan dengan simbol kompilasi bersyarat yang dideklarasikan dalam atributnya Conditional .

Contoh:

class Eg
{
    [Conditional("ALPHA")]
    [Conditional("BETA")]
    public static void M()
    {
        // ...
    }
}

menyatakan Eg.M sebagai metode bersyarat yang terkait dengan dua simbol kompilasi bersyarat ALPHA dan BETA.

contoh akhir

Panggilan ke metode bersyarkat disertakan jika satu atau beberapa simbol kompilasi bersyar terkait didefinisikan pada titik panggilan, jika tidak, panggilan dihilangkan.

Metode bersyukur tunduk pada pembatasan berikut:

  • Metode bersyarah harus menjadi metode dalam class_declaration atau struct_declaration. Kesalahan waktu kompilasi terjadi jika Conditional atribut ditentukan pada metode dalam deklarasi antarmuka.
  • Metode bersyarah harus memiliki jenis pengembalian .void
  • Metode kondisional tidak boleh ditandai dengan pengubah override . Namun, metode kondisional dapat ditandai dengan pengubah virtual . Penimpaan metode tersebut secara implisit kondisional, dan tidak boleh ditandai secara eksplisit dengan Conditional atribut .
  • Metode kondisional tidak boleh menjadi implementasi metode antarmuka. Jika tidak, terjadi kesalahan waktu kompilasi.
  • Parameter metode bersyarah tidak boleh berupa parameter output.

Selain itu, kesalahan waktu kompilasi terjadi jika delegasi dibuat dari metode bersyarah.

Contoh: Contoh

#define DEBUG
using System;
using System.Diagnostics;

class Class1
{
    [Conditional("DEBUG")]
    public static void M()
    {
        Console.WriteLine("Executed Class1.M");
    }
}

class Class2
{
    public static void Test()
    {
        Class1.M();
    }
}

menyatakan sebagai metode bersyarat Class1.M . Class2Metode memanggil Test metode ini. Karena simbol DEBUG kompilasi bersyarat didefinisikan, jika Class2.Test dipanggil, simbol tersebut akan memanggil M. Jika simbol DEBUG belum ditentukan, maka Class2.Test tidak akan memanggil Class1.M.

contoh akhir

Penting untuk dipahami bahwa penyertaan atau pengecualian panggilan ke metode kondisional dikendalikan oleh simbol kompilasi kondisional pada saat panggilan.

Contoh: Dalam kode berikut

// File Class1.cs:
using System;
using System.Diagnostics;
class Class1
{
    [Conditional("DEBUG")]
    public static void F()
    {
        Console.WriteLine("Executed Class1.F");
    }
}

// File Class2.cs:
#define DEBUG
class Class2
{
    public static void G()
    {
        Class1.F(); // F is called
    }
}

// File Class3.cs:
#undef DEBUG
class Class3
{
    public static void H()
    {
        Class1.F(); // F is not called
    }
}

kelas Class2 dan Class3 masing-masing berisi panggilan ke metode Class1.Fkondisional , yang bersyukur berdasarkan apakah didefinisikan atau tidak DEBUG . Karena simbol ini didefinisikan dalam konteks Class2 tetapi tidak Class3, panggilan ke F dalam Class2 disertakan, sementara panggilan ke F dihilangkan Class3 .

contoh akhir

Penggunaan metode kondisional dalam rantai warisan dapat membingungkan. Panggilan yang dilakukan ke metode bersyarah melalui base, dari formulir base.M, tunduk pada aturan panggilan metode kondisi normal.

Contoh: Dalam kode berikut

// File Class1.cs
using System;
using System.Diagnostics;
class Class1
{
    [Conditional("DEBUG")]
    public virtual void M() => Console.WriteLine("Class1.M executed");
}

// File Class2.cs
class Class2 : Class1
{
    public override void M()
    {
        Console.WriteLine("Class2.M executed");
        base.M(); // base.M is not called!
    }
}

// File Class3.cs
#define DEBUG
class Class3
{
    public static void Main()
    {
        Class2 c = new Class2();
        c.M(); // M is called
    }
}

Class2 termasuk panggilan ke yang M ditentukan di kelas dasarnya. Panggilan ini dihilangkan karena metode dasar bersyukur berdasarkan keberadaan simbol DEBUG, yang tidak terdefinisi. Dengan demikian, metode menulis ke konsol "Class2.M executed" saja. Penggunaan pp_declarationyang berdasar dapat menghilangkan masalah tersebut.

contoh akhir

22.5.3.3 Kelas atribut kondisional

Kelas atribut (§22.2) yang didekorasi dengan satu atau beberapa Conditional atribut adalah kelas atribut kondisional. Kelas atribut bersyarat dengan demikian dikaitkan dengan simbol kompilasi bersyarat yang dideklarasikan dalam atributnya Conditional .

Contoh:

[Conditional("ALPHA")]
[Conditional("BETA")]
public class TestAttribute : Attribute {}

menyatakan TestAttribute sebagai kelas atribut bersyarat yang terkait dengan simbol kompilasi bersyarat ALPHA dan BETA.

contoh akhir

Spesifikasi atribut (§22,3) dari atribut bersyarkat disertakan jika satu atau beberapa simbol kompilasi bersyarkat terkait didefinisikan pada titik spesifikasi, jika tidak, spesifikasi atribut dihilangkan.

Penting untuk dicatat bahwa penyertaan atau pengecualian spesifikasi atribut dari kelas atribut bersyarkat dikontrol oleh simbol kompilasi bersyarkat pada titik spesifikasi.

Contoh: Dalam contoh

// File Test.cs:
using System;
using System.Diagnostics;
[Conditional("DEBUG")]
public class TestAttribute : Attribute {}

// File Class1.cs:
#define DEBUG
[Test] // TestAttribute is specified
class Class1 {}

// File Class2.cs:
#undef DEBUG
[Test] // TestAttribute is not specified
class Class2 {}

kelas Class1 dan Class2 masing-masing didekorasi dengan atribut Test, yang bersyukur berdasarkan apakah ditentukan atau tidak DEBUG . Karena simbol ini didefinisikan dalam konteks Class1 tetapi tidak Class2, spesifikasi atribut Uji pada Class1 disertakan, sementara spesifikasi Test atribut pada dihilangkan Class2 .

contoh akhir

22.5.4 Atribut Usang

Atribut Obsolete digunakan untuk menandai jenis dan anggota jenis yang seharusnya tidak lagi digunakan.

Jika program menggunakan jenis atau anggota yang dihiasi dengan atribut Obsolete, pengkompilasi akan mengeluarkan peringatan atau kesalahan. Secara khusus, kompilator harus mengeluarkan peringatan jika tidak ada parameter kesalahan yang disediakan, atau jika parameter kesalahan disediakan dan memiliki nilai false. Kompilator akan mengeluarkan kesalahan jika parameter kesalahan ditentukan dan memiliki nilai true.

Contoh: Dalam kode berikut

[Obsolete("This class is obsolete; use class B instead")]
class A
{
    public void F() {}
}

class B
{
    public void F() {}
}

class Test
{
    static void Main()
    {
        A a = new A(); // Warning
        a.F();
    }
}

kelas A dihiasi dengan Obsolete atribut . Setiap penggunaan A dalam Main menghasilkan peringatan yang mencakup pesan yang ditentukan, "Kelas ini usang; gunakan kelas B sebagai gantinya".

contoh akhir

22.5.5 Atribut AsyncMethodBuilder

Atribut ini dijelaskan dalam §15.14.1.

22.5.6 Atribut caller-info

22.5.6.1 Umum

Untuk tujuan seperti pengelogan dan pelaporan, terkadang berguna bagi anggota fungsi untuk mendapatkan informasi waktu kompilasi tertentu tentang kode panggilan. Atribut caller-info menyediakan cara untuk meneruskan informasi tersebut secara transparan.

Ketika parameter opsional diannotasi dengan salah satu atribut caller-info, menghilangkan argumen yang sesuai dalam panggilan tidak selalu menyebabkan nilai parameter default diganti. Sebaliknya, jika informasi yang ditentukan tentang konteks panggilan tersedia, informasi tersebut akan diteruskan sebagai nilai argumen.

Contoh:

public void Log(
    [CallerLineNumber] int line = -1,
    [CallerFilePath] string path = null,
    [CallerMemberName] string name = null
)
{
    Console.WriteLine((line < 0) ? "No line" : "Line "+ line);
    Console.WriteLine((path == null) ? "No file path" : path);
    Console.WriteLine((name == null) ? "No member name" : name);
}

Panggilan ke Log() tanpa argumen akan mencetak nomor baris dan jalur file panggilan, serta nama anggota tempat panggilan terjadi.

contoh akhir

Atribut caller-info dapat terjadi pada parameter opsional di mana saja, termasuk dalam deklarasi delegasi. Namun, atribut caller-info tertentu memiliki batasan pada jenis parameter yang dapat mereka atribut, sehingga akan selalu ada konversi implisit dari nilai yang diganti ke jenis parameter.

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

Informasi penelepon tidak memengaruhi resolusi kelebihan beban. Karena parameter opsional yang dikaitkan masih dihilangkan dari kode sumber pemanggil, resolusi kelebihan beban mengabaikan parameter tersebut dengan cara yang sama mengabaikan parameter opsional lain yang dihilangkan (§12.6.4).

Informasi penelepon hanya diganti ketika fungsi secara eksplisit dipanggil dalam kode sumber. Pemanggilan implisit seperti panggilan konstruktor induk implisit tidak memiliki lokasi sumber dan tidak akan menggantikan informasi pemanggil. Selain itu, panggilan yang terikat secara dinamis tidak akan menggantikan informasi pemanggil. Ketika parameter atribut caller-info dihilangkan dalam kasus seperti itu, nilai default parameter yang ditentukan digunakan sebagai gantinya.

Satu pengecualian adalah ekspresi kueri. Ini dianggap sebagai ekspansi syntactic, dan jika panggilan yang mereka perluas untuk menghilangkan parameter opsional dengan atribut caller-info, informasi penelepon akan diganti. Lokasi yang digunakan adalah lokasi klausa kueri tempat panggilan dihasilkan.

Jika lebih dari satu atribut caller-info ditentukan pada parameter tertentu, atribut tersebut dikenali dalam urutan berikut: CallerLineNumber, , CallerFilePathCallerMemberName. Pertimbangkan deklarasi parameter berikut:

[CallerMemberName, CallerFilePath, CallerLineNumber] object p = ...

CallerLineNumber lebih diutamakan, dan dua atribut lainnya diabaikan. Jika CallerLineNumber dihilangkan, CallerFilePath akan diutamakan, dan CallerMemberName akan diabaikan. Urutan leksikal atribut ini tidak relevan.

22.5.6.2 Atribut CallerLineNumber

Atribut System.Runtime.CompilerServices.CallerLineNumberAttribute diizinkan pada parameter opsional ketika ada konversi implisit standar (§10.4.2) dari nilai int.MaxValue konstanta ke jenis parameter. Ini memastikan bahwa nomor baris non-negatif hingga nilai tersebut dapat diteruskan tanpa kesalahan.

Jika pemanggilan fungsi dari lokasi dalam kode sumber menghilangkan parameter opsional dengan CallerLineNumberAttribute, maka literal numerik yang mewakili nomor baris lokasi tersebut digunakan sebagai argumen untuk pemanggilan alih-alih nilai parameter default.

Jika pemanggilan mencakup beberapa baris, baris yang dipilih bergantung pada implementasi.

Nomor baris dapat dipengaruhi oleh #line arahan (§6.5.8).

22.5.6.3 Atribut CallerFilePath

Atribut System.Runtime.CompilerServices.CallerFilePathAttribute diizinkan pada parameter opsional ketika ada konversi implisit standar (§10.4.2) dari string ke jenis parameter.

Jika pemanggilan fungsi dari lokasi dalam kode sumber menghilangkan parameter opsional dengan CallerFilePathAttribute, maka string literal yang mewakili jalur file lokasi tersebut digunakan sebagai argumen untuk pemanggilan alih-alih nilai parameter default.

Format jalur file bergantung pada implementasi.

Jalur file dapat dipengaruhi oleh #line arahan (§6.5.8).

22.5.6.4 Atribut CallerMemberName

Atribut System.Runtime.CompilerServices.CallerMemberNameAttribute diizinkan pada parameter opsional ketika ada konversi implisit standar (§10.4.2) dari string ke jenis parameter.

Jika pemanggilan fungsi dari lokasi dalam isi anggota fungsi atau dalam atribut yang diterapkan ke anggota fungsi itu sendiri atau jenis pengembaliannya, parameter atau parameter jenis dalam kode sumber menghilangkan parameter opsional dengan CallerMemberNameAttribute, maka string harfiah yang mewakili nama anggota tersebut digunakan sebagai argumen untuk pemanggilan alih-alih nilai parameter default.

Untuk pemanggilan yang terjadi dalam metode generik, hanya nama metode itu sendiri yang digunakan, tanpa daftar parameter jenis.

Untuk pemanggilan yang terjadi dalam implementasi anggota antarmuka eksplisit, hanya nama metode itu sendiri yang digunakan, tanpa kualifikasi antarmuka sebelumnya.

Untuk pemanggilan yang terjadi dalam properti atau aksesor peristiwa, nama anggota yang digunakan adalah properti atau peristiwa itu sendiri.

Untuk pemanggilan yang terjadi dalam pengakses pengindeks, nama anggota yang digunakan adalah yang disediakan oleh IndexerNameAttribute (§22.6) pada anggota pengindeks, jika ada, atau nama Item default sebaliknya.

Untuk pemanggilan yang terjadi dalam penginisialisasi bidang atau peristiwa, nama anggota yang digunakan adalah nama bidang atau peristiwa yang sedang diinisialisasi.

Untuk pemanggilan yang terjadi dalam deklarasi konstruktor instans, konstruktor statis, finalizer, dan operator nama anggota yang digunakan bergantung pada implementasi.

22.5.7 Atribut analisis kode

22.5.7.1 Umum

Atribut di bagian ini digunakan untuk memberikan informasi tambahan untuk mendukung pengkompilasi yang menyediakan diagnostik null-state dan null -state (§8.9.5). Pengkompilasi tidak diperlukan untuk melakukan diagnostik status null. Kehadiran atau tidak adanya atribut ini tidak memengaruhi bahasa atau perilaku program. Pengkompilasi yang tidak menyediakan diagnostik status null harus membaca dan mengabaikan keberadaan atribut ini. Pengkompilasi yang menyediakan diagnostik status null harus menggunakan arti yang ditentukan di bagian ini untuk salah satu atribut ini yang digunakannya untuk menginformasikan diagnostiknya.

Atribut analisis kode dideklarasikan dalam namespace System.Diagnostics.CodeAnalysis.

Atribut Arti
AllowNull (§22.5.7.2) Argumen yang tidak dapat diubah ke null mungkin null.
DisallowNull (§22.5.7.3) Argumen nullable tidak boleh null.
MaybeNull (§22.5.7.6) Nilai pengembalian yang tidak dapat diubah ke null mungkin null.
NotNull (§22.5.7.8) Nilai pengembalian null tidak akan pernah null.
MaybeNullWhen (§22.5.7.7) Argumen yang tidak dapat diubah ke null mungkin null saat metode mengembalikan nilai bool yang ditentukan.
NotNullWhen (§22.5.7.10) Argumen nullable tidak akan null ketika metode mengembalikan nilai yang ditentukan bool .
NotNullIfNotNull (§22.5.7.9) Nilai yang dikembalikan tidak null jika argumen untuk parameter yang ditentukan tidak null.
DoesNotReturn (§22.5.7.4) Metode ini tidak pernah kembali.
DoesNotReturnIf (§22.5.7.5) Metode ini tidak pernah mengembalikan jika parameter terkait bool memiliki nilai yang ditentukan.

Bagian berikut dalam §22.5.7.1 bersifat normatif kondisional.

22.5.7.2 Atribut AllowNull

Menentukan bahwa nilai null diizinkan sebagai input meskipun jenis yang sesuai melarangnya.

Contoh: Pertimbangkan properti baca/tulis berikut yang tidak pernah ditampilkan null karena memiliki nilai default yang wajar. Namun, pengguna dapat memberikan null kepada aksesor yang ditetapkan untuk mengatur properti ke nilai default tersebut.

#nullable enable
public class X
{
    [AllowNull]
    public string ScreenName
    {
        get => _screenName;
        set => _screenName = value ?? GenerateRandomScreenName();
    }
    private string _screenName = GenerateRandomScreenName();
    private static string GenerateRandomScreenName() => ...;
}

Mengingat penggunaan aksesor set properti tersebut berikut

var v = new X();
v.ScreenName = null;   // may warn without attribute AllowNull

tanpa atribut, pengompilasi dapat menghasilkan peringatan karena properti bertipe non-nullable tampaknya diatur ke nilai null. Kehadiran atribut menekan peringatan tersebut. contoh akhir

22.5.7.3 Atribut DisallowNull

Menentukan bahwa nilai null tidak diizinkan sebagai input meskipun jenis yang sesuai mengizinkannya.

Contoh: Pertimbangkan properti berikut di mana null adalah nilai default, tetapi klien hanya dapat mengaturnya ke nilai non-null.

#nullable enable
public class X
{
    [DisallowNull]
    public string? ReviewComment
    {
        get => _comment;
        set => _comment = value ?? throw new ArgumentNullException(nameof(value),
           "Cannot set to null");
    }
    private string? _comment = default;
}

Get accessor dapat mengembalikan nilai default null, sehingga pengkompilasi dapat memperingatkan bahwa itu harus diperiksa sebelum akses. Selain itu, ini memperingatkan penelepon bahwa, meskipun bisa null, penelepon tidak boleh secara eksplisit mengaturnya ke null. contoh akhir

22.5.7.4 Atribut DoesNotReturn

Menentukan bahwa metode tertentu tidak pernah kembali.

Contoh: Pertimbangkan hal berikut:

public class X
{
    [DoesNotReturn]
    private void FailFast() =>
        throw new InvalidOperationException();

    public void SetState(object? containedField)
    {
        if ((!isInitialized) || (containedField == null))
        {
            FailFast();
        }
        // null check not needed.
        _field = containedField;
    }

    private bool isInitialized = false;
    private object _field;
}

Kehadiran atribut membantu pengkompilasi dengan sejumlah cara. Pertama, kompilator dapat mengeluarkan peringatan jika ada jalur di mana metode dapat keluar tanpa melemparkan pengecualian. Kedua, kompilator dapat menekan peringatan nullable dalam kode apa pun setelah panggilan ke metode tersebut, sampai klausa tangkapan yang sesuai ditemukan. Ketiga, kode yang tidak dapat dijangkau tidak akan memengaruhi status null apa pun.

Atribut tidak mengubah keterjangkauan (§13.2) atau analisis penetapan pasti (§9,4) berdasarkan keberadaan atribut ini. Ini hanya digunakan untuk memengaruhi peringatan nullability. contoh akhir

22.5.7.5 Atribut DoesNotReturnIf

Menentukan bahwa metode tertentu tidak pernah mengembalikan jika parameter terkait bool memiliki nilai yang ditentukan.

Contoh: Pertimbangkan hal berikut:

#nullable enable
public class X
{
    private void ThrowIfNull([DoesNotReturnIf(true)] bool isNull, string argumentName)
    {
        if (!isNull)
        {
            throw new ArgumentException(argumentName, $"argument {argumentName} can't be null");
        }
    }

    public void SetFieldState(object containedField)
    {
        ThrowIfNull(containedField == null, nameof(containedField));
        // unreachable code when "isInitialized" is false:
        _field = containedField;
    }

    private bool isInitialized = false;
    private object _field = default!;
}

contoh akhir

22.5.7.6 Atribut MaybeNull

Menentukan bahwa nilai pengembalian yang tidak dapat diubah ke null mungkin null.

Contoh: Pertimbangkan metode generik berikut:

#nullable enable
public T? Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }

Ide dari kode ini adalah bahwa jika T digantikan oleh string, T? menjadi anotasi nullable. Namun, kode ini tidak legal karena T tidak dibatasi untuk menjadi jenis referensi. Namun, menambahkan atribut ini memecahkan masalah:

#nullable enable
[return: MaybeNull]
public T Find<T>(IEnumerable<T> sequence, Func<T, bool> predicate) { ... }

Atribut menginformasikan kepada penelepon bahwa kontrak menyiratkan jenis yang tidak dapat diubah ke null, tetapi nilai pengembalian sebenarnya mungkin null. contoh akhir

22.5.7.7 Atribut MaybeNullWhen

Menentukan bahwa argumen yang tidak dapat diubah ke null mungkin null ketika metode mengembalikan nilai yang ditentukan bool . Ini mirip MaybeNull dengan atribut (§22.5.7.6), tetapi menyertakan parameter untuk nilai pengembalian yang ditentukan.

22.5.7.8 Atribut NotNull

Menentukan bahwa nilai nullable tidak akan null pernah jika metode mengembalikan (bukan melemparkan).

Contoh: Pertimbangkan hal berikut:

#nullable enable
public static void ThrowWhenNull([NotNull] object? value, string valueExpression = "") =>
    _ = value ?? throw new ArgumentNullException(valueExpression);

public static void LogMessage(string? message)
{
    ThrowWhenNull(message, nameof(message));
    Console.WriteLine(message.Length);
}

Ketika jenis referensi null diaktifkan, metode ThrowWhenNull dikompilasi tanpa peringatan. Ketika metode itu kembali, argumen dijamin value bukan null. Namun, dapat diterima untuk memanggil ThrowWhenNull dengan referensi null. contoh akhir

22.5.7.9 Atribut NotNullIfNotNull

Menentukan bahwa nilai pengembalian bukan null jika argumen untuk parameter yang ditentukan bukan null.

Contoh: Status null dari nilai pengembalian dapat bergantung pada status null dari satu atau beberapa argumen. Untuk membantu analisis pengkompilasi ketika metode selalu mengembalikan nilai non-null ketika argumen tertentu tidak null atribut NotNullIfNotNull dapat digunakan. Pertimbangkan kode berikut:

#nullable enable
string GetTopLevelDomainFromFullUrl(string url) { ... }

url Jika argumen tidak null, null tidak dikembalikan. Ketika referensi nullable diaktifkan, tanda tangan tersebut berfungsi dengan benar, asalkan API tidak pernah menerima argumen null. Namun, jika argumen bisa null, maka nilai yang dikembalikan juga bisa null. Untuk mengekspresikan kontrak tersebut dengan benar, anotasi metode ini sebagai berikut:

#nullable enable
[return: NotNullIfNotNull("url")]
string? GetTopLevelDomainFromFullUrl(string? url) { ... }

contoh akhir

22.5.7.10 Atribut NotNullWhen

Menentukan bahwa argumen nullable tidak akan ketika null metode mengembalikan nilai yang ditentukan bool .

Contoh: Metode String.IsNullOrEmpty(String) pustaka mengembalikan true saat argumen adalah null atau string kosong. Ini adalah bentuk pemeriksaan null: Penelepon tidak perlu memeriksa argumen null jika metode mengembalikan false. Untuk membuat metode seperti ini sadar nullable, buat jenis parameter sebagai jenis referensi nullable, dan tambahkan atribut NotNullWhen:

#nullable enable
bool IsNullOrEmpty([NotNullWhen(false)] string? value) { ... }

contoh akhir

22.6 Atribut untuk interoperatur

Untuk interoperatasi dengan bahasa lain, pengindeks dapat diimplementasikan menggunakan properti terindeks. Jika tidak ada IndexerName atribut yang ada untuk pengindeks, maka nama Item tersebut digunakan secara default. Atribut ini IndexerName memungkinkan pengembang untuk mengambil alih default ini dan menentukan nama yang berbeda.

Contoh: Secara default, nama pengindeks adalah Item. Ini dapat ditimpa, sebagai berikut:

[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this[int index]
{
    get { ... }
    set { ... }
}

Sekarang, nama pengindeks adalah TheItem.

contoh akhir