Bagikan melalui


8 Jenis

8.1 Umum

Jenis bahasa C# dibagi menjadi dua kategori utama: jenis referensi dan jenis nilai. Jenis nilai dan jenis referensi mungkin jenis generik, yang mengambil satu atau beberapa parameter tipe. Parameter jenis dapat menunjuk jenis nilai dan jenis referensi.

type
    : reference_type
    | value_type
    | type_parameter
    | pointer_type     // unsafe code support
    ;

pointer_type (§23.3) hanya tersedia dalam kode tidak aman (§23).

Jenis nilai berbeda dari jenis referensi dalam hal variabel dari jenis nilai langsung mengandung datanya, sementara variabel dari jenis referensi menyimpan referensi ke data mereka, yang terakhir ini dikenal sebagai objek. Dengan jenis referensi, dimungkinkan bagi dua variabel untuk mereferensikan objek yang sama, dan dengan demikian mungkin untuk operasi pada satu variabel untuk memengaruhi objek yang dirujuk oleh variabel lain. Dengan jenis nilai, variabel masing-masing memiliki salinan data mereka sendiri, dan tidak mungkin bagi operasi pada satu untuk memengaruhi yang lain.

Catatan: Ketika variabel adalah parameter referensi atau output, variabel tersebut tidak memiliki penyimpanannya sendiri tetapi mereferensikan penyimpanan variabel lain. Dalam hal ini, variabel ref atau out secara efektif merupakan alias untuk variabel lain dan bukan variabel yang berbeda. catatan akhir

Sistem jenis C#disatukan sehingga nilai dari jenis apa pun dapat diperlakukan sebagai objek. Setiap jenis dalam C# secara langsung atau tidak langsung berasal dari jenis kelas object, dan object merupakan kelas dasar utama dari semua jenis. Nilai jenis referensi diperlakukan sebagai objek hanya dengan melihat nilai sebagai jenis object. Nilai dari jenis nilai diperlakukan sebagai objek dengan melakukan operasi pembungkusan dan pembongkaran (§8.3.13).

Untuk kenyamanan, sepanjang spesifikasi ini, beberapa nama jenis pustaka ditulis tanpa menggunakan kualifikasi nama lengkapnya. Lihat §C.5 untuk informasi selengkapnya.

8.2 Jenis referensi

8.2.1 Umum

Jenis referensi adalah jenis kelas, jenis antarmuka, jenis array, jenis delegasi, atau jenisnya dynamic . Untuk setiap jenis referensi yang tidak dapat diubah ke null, ada jenis referensi null yang sesuai yang dicatat dengan menambahkan ke ? nama jenis.

reference_type
    : non_nullable_reference_type
    | nullable_reference_type
    ;

non_nullable_reference_type
    : class_type
    | interface_type
    | array_type
    | delegate_type
    | 'dynamic'
    ;

class_type
    : type_name
    | 'object'
    | 'string'
    ;

interface_type
    : type_name
    ;

array_type
    : non_array_type rank_specifier+
    ;

non_array_type
    : value_type
    | class_type
    | interface_type
    | delegate_type
    | 'dynamic'
    | type_parameter
    | pointer_type      // unsafe code support
    ;

rank_specifier
    : '[' ','* ']'
    ;

delegate_type
    : type_name
    ;

nullable_reference_type
    : non_nullable_reference_type nullable_type_annotation
    ;

nullable_type_annotation
    : '?'
    ;

pointer_type hanya tersedia dalam kode yang tidak aman (§23.3). nullable_reference_type dibahas lebih lanjut dalam §8.9.

Nilai jenis referensi adalah referensi ke instans jenis , yang terakhir dikenal sebagai objek. Nilai null khusus kompatibel dengan semua jenis referensi dan menunjukkan tidak adanya instans.

8.2.2 Jenis kelas

Jenis kelas menentukan struktur data yang berisi anggota data (konstanta dan bidang), anggota fungsi (metode, properti, peristiwa, pengindeks, operator, konstruktor instans, finalizer, dan konstruktor statis), dan jenis berlapis. Jenis kelas mendukung pewarisan, mekanisme di mana kelas turunan dapat memperluas dan mengkhususkan kelas dasar. Instans jenis kelas dibuat menggunakan object_creation_expression s (§12.8.17.2).

Jenis kelas dijelaskan dalam §15.

Jenis kelas tertentu yang telah ditentukan sebelumnya memiliki arti khusus dalam bahasa C#, seperti yang dijelaskan dalam tabel di bawah ini.

Jenis kelas Keterangan
System.Object Kelas dasar utama dari semua tipe lainnya. Lihat §8.2.3.
System.String Jenis string bahasa C#. Lihat §8.2.5.
System.ValueType Kelas dasar dari semua jenis nilai. Lihat §8.3.2.
System.Enum Kelas dasar dari semua enum tipe. Lihat §19,5.
System.Array Kelas dasar dari semua jenis array. Lihat §17.2.2.
System.Delegate Kelas dasar dari semua delegate tipe. Lihat §20.1.
System.Exception Kelas dasar dari semua jenis pengecualian. Lihat §21.3.

8.2.3 Jenis objek

Jenis object kelas adalah kelas dasar utama dari semua jenis lainnya. Setiap jenis dalam C# secara langsung atau tidak langsung berasal dari object jenis kelas.

Kata kunci object hanyalah alias untuk kelas System.Objectyang telah ditentukan sebelumnya.

8.2.4 Jenis dinamis

Jenisnya dynamic , seperti object, dapat mereferensikan objek apa pun. Ketika operasi diterapkan ke ekspresi jenis dynamic, resolusinya ditangguhkan hingga program dijalankan. Dengan demikian, jika operasi tidak dapat diterapkan secara sah ke objek yang dirujuk, tidak ada kesalahan yang diberikan selama kompilasi. Sebaliknya, pengecualian akan dilepaskan ketika penyelesaian operasi gagal selama waktu eksekusi.

Jenis ini dynamic dijelaskan lebih lanjut dalam §8.7, dan pengikatan dinamis dalam §12.3.1.

8.2.5 Jenis string

Jenisnya string adalah jenis kelas tertutup yang mewarisi langsung dari object. Instans string kelas mewakili string karakter Unicode.

Nilai jenis string dapat ditulis sebagai literal string (§6.4.5.6).

Kata kunci string hanyalah alias untuk kelas System.Stringyang telah ditentukan sebelumnya.

8.2.6 Jenis antarmuka

Antarmuka mendefinisikan kontrak. Kelas atau struktur yang mengimplementasikan antarmuka harus mematuhi kontraknya. Antarmuka dapat mewarisi dari beberapa antarmuka dasar, dan kelas atau struktur dapat mengimplementasikan beberapa antarmuka.

Jenis antarmuka dijelaskan dalam §18.

8.2.7 Jenis array

Array adalah struktur data yang berisi nol atau lebih variabel, yang diakses melalui indeks yang dihitung. Variabel yang terkandung dalam array, juga disebut elemen array, semuanya dari jenis yang sama, dan jenis ini disebut jenis elemen array.

Jenis array dijelaskan dalam §17.

8.2.8 Jenis delegasi

Delegat adalah struktur data yang merujuk pada satu atau beberapa metode. Untuk metode instance, ini juga mengacu pada instance objek yang bersesuaian.

Catatan: Yang paling setara dengan delegasi di C atau C++ adalah penunjuk fungsi, tetapi sedangkan penunjuk fungsi hanya dapat mereferensikan fungsi statis, delegasi dapat mereferensikan metode statis dan instans. Dalam kasus terakhir, delegasi tidak hanya menyimpan referensi ke titik masuk metode, tetapi juga referensi ke instans objek untuk memanggil metode . catatan akhir

Jenis delegasi dijelaskan dalam §20.

8.3 Jenis nilai

8.3.1 Umum

Jenis nilai adalah jenis struct atau jenis enumerasi. C# menyediakan sekumpulan jenis struct yang telah ditentukan sebelumnya yang disebut jenis sederhana. Jenis sederhana diidentifikasi melalui kata kunci.

value_type
    : non_nullable_value_type
    | nullable_value_type
    ;

non_nullable_value_type
    : struct_type
    | enum_type
    ;

struct_type
    : type_name
    | simple_type
    | tuple_type
    ;

simple_type
    : numeric_type
    | 'bool'
    ;

numeric_type
    : integral_type
    | floating_point_type
    | 'decimal'
    ;

integral_type
    : 'sbyte'
    | 'byte'
    | 'short'
    | 'ushort'
    | 'int'
    | 'uint'
    | 'long'
    | 'ulong'
    | 'char'
    ;

floating_point_type
    : 'float'
    | 'double'
    ;

tuple_type
    : '(' tuple_type_element (',' tuple_type_element)+ ')'
    ;
    
tuple_type_element
    : type identifier?
    ;
    
enum_type
    : type_name
    ;

nullable_value_type
    : non_nullable_value_type nullable_type_annotation
    ;

Tidak seperti variabel jenis referensi, variabel jenis nilai hanya dapat berisi nilai null jika jenis nilai adalah jenis nilai yang dapat diubah ke null (§8.3.12). Untuk setiap jenis nilai yang tidak dapat diubah ke null, ada jenis nilai nullable yang sesuai yang menunjukkan set nilai yang sama ditambah nilai null.

Penugasan suatu nilai ke variabel tipe nilai akan membuat sebuah salinan dari nilai yang ditugaskan. Ini berbeda dari penugasan ke variabel jenis referensi, yang menyalin referensi tetapi bukan objek yang diidentifikasi oleh referensi.

8.3.2 Jenis System.ValueType

Semua jenis nilai secara implisit mewarisi dari classSystem.ValueType, yang pada gilirannya, mewarisi dari kelas object. Jenis apa pun tidak dimungkinkan untuk berasal dari jenis nilai, dan jenis nilai dengan demikian secara implisit disegel (§15.2.2.3).

Perhatikan bahwa System.ValueType itu bukan value_type. Sebaliknya, ini adalah class_type dari mana semua jenis nilai secara otomatis diturunkan.

8.3.3 Konstruktor bawaan

Semua jenis nilai secara implisit mendeklarasikan konstruktor instans tanpa parameter publik yang disebut konstruktor default. Konstruktor bawaan mengembalikan instans yang diinisialisasi ke nol, dikenal sebagai nilai bawaan untuk jenis nilai.

  • Untuk semua simple_type, nilai default adalah nilai yang dihasilkan oleh pola bit dari semua nol:
    • Untuk sbyte, , byteshort, ushort, int, uint, long, dan ulong, nilai defaultnya adalah 0.
    • Untuk char, nilai defaultnya adalah '\x0000'.
    • Untuk float, nilai defaultnya adalah 0.0f.
    • Untuk double, nilai defaultnya adalah 0.0d.
    • Untuk decimal, nilai defaultnya adalah 0m (yaitu, nilai nol dengan skala 0).
    • Untuk bool, nilai defaultnya adalah false.
    • Untuk enum_typeE, nilai default adalah 0, yang dikonversi ke jenis E.
  • Untuk struct_type, nilai default adalah nilai yang dihasilkan dengan mengatur semua bidang jenis nilai ke nilai defaultnya dan semua bidang jenis referensi ke null.
  • Nilai default untuk nullable_value_type adalah instance dari tipe tersebut yang HasValue propertinya salah. Nilai default juga dikenal sebagai nilai null dari jenis nilai nullable. Mencoba membaca properti dari nilai tersebut Value menyebabkan pengecualian jenis System.InvalidOperationException dilemparkan (§8.3.12).

Seperti konstruktor instans lainnya, konstruktor default dari jenis nilai dipanggil menggunakan new operator.

Catatan: Untuk alasan efisiensi, persyaratan ini tidak dimaksudkan untuk benar-benar memiliki implementasi yang menghasilkan panggilan konstruktor. Untuk jenis nilai, ekspresi nilai default (§12.8.21) menghasilkan hasil yang sama seperti menggunakan konstruktor default. catatan akhir

Contoh: Dalam kode di bawah ini, variabel i, j dan k semuanya diinisialisasi menjadi nol.

class A
{
    void F()
    {
        int i = 0;
        int j = new int();
        int k = default(int);
    }
}

contoh akhir

Karena setiap jenis nilai secara implisit memiliki konstruktor instans tanpa parameter publik, tidak dimungkinkan bagi jenis struct untuk berisi deklarasi eksplisit konstruktor tanpa parameter. Namun, jenis struct diizinkan untuk mendeklarasikan konstruktor instans berparameter (§16.4.9).

8.3.4 Jenis struktur

Jenis struct adalah jenis nilai yang dapat mendeklarasikan konstanta, bidang, metode, properti, peristiwa, pengindeks, operator, konstruktor instans, konstruktor statis, dan jenis berlapis. Deklarasi jenis struct dijelaskan dalam §16.

8.3.5 Jenis sederhana

C# menyediakan sekumpulan jenis yang telah struct ditentukan sebelumnya yang disebut jenis sederhana. Jenis sederhana diidentifikasi melalui kata kunci, tetapi kata kunci ini hanyalah alias untuk jenis yang telah struct ditentukan sebelumnya di System namespace, seperti yang dijelaskan dalam tabel di bawah ini.

Kata kunci Tipe beraliaskan
sbyte System.SByte
byte System.Byte
short System.Int16
ushort System.UInt16
int System.Int32
uint System.UInt32
long System.Int64
ulong System.UInt64
char System.Char
float System.Single
double System.Double
bool System.Boolean
decimal System.Decimal

Karena jenis sederhana pada dasarnya adalah alias dari jenis struct, setiap jenis sederhana memiliki anggota.

Contoh: int memiliki anggota yang dinyatakan di System.Int32 dan anggota yang diwarisi dari System.Object, dan pernyataan berikut diperbolehkan:

int i = int.MaxValue;      // System.Int32.MaxValue constant
string s = i.ToString();   // System.Int32.ToString() instance method
string t = 123.ToString(); // System.Int32.ToString() instance method

contoh akhir

Catatan: Jenis sederhana berbeda dari jenis struct lainnya karena mereka mengizinkan operasi tambahan tertentu:

  • Sebagian besar jenis sederhana mengizinkan nilai untuk dibuat dengan menulis literal (§6.4.5), meskipun C# tidak membuat ketentuan untuk literal jenis struktur secara umum. Contoh: 123 adalah jenis harfiah int dan 'a' merupakan jenis charharfiah . contoh akhir
  • Ketika operand ekspresi adalah semua konstanta jenis sederhana, dimungkinkan bagi pengkompilasi untuk mengevaluasi ekspresi pada waktu kompilasi. Ekspresi seperti itu dikenal sebagai constant_expression (§12.23). Ekspresi yang melibatkan operator yang ditentukan oleh jenis struct lainnya tidak dianggap sebagai ekspresi konstanta
  • Melalui const deklarasi, dimungkinkan untuk mendeklarasikan konstanta dari jenis sederhana (§15,4). Tidak dimungkinkan untuk memiliki konstanta jenis struct lainnya, tetapi efek serupa disediakan oleh bidang readonly statis.
  • Konversi yang melibatkan jenis sederhana dapat berpartisipasi dalam evaluasi operator konversi yang ditentukan oleh jenis struktur lain, tetapi operator konversi yang ditentukan pengguna tidak pernah dapat berpartisipasi dalam evaluasi operator konversi lain yang ditentukan pengguna (§10.5.3).

akhiri catatan.

8.3.6 Jenis integral

C# mendukung sembilan jenis integral: sbyte, , byte, short, ushortint, uint, long, ulong, , dan char. Jenis integral memiliki ukuran dan rentang nilai berikut:

  • Tipe sbyte mewakili bilangan bulat 8-bit yang ditandatangani dengan nilai dari -128 hingga 127, termasuk.
  • Jenis byte mewakili bilangan bulat 8-bit yang tidak bertanda dengan nilai dari 0 hingga 255, termasuk.
  • Jenis short mewakili bilangan bulat 16-bit berjenis signed dengan nilai dari -32768 hingga 32767.
  • Jenis ushort mewakili bilangan bulat 16-bit tanpa tanda dengan nilai dari 0 hingga 65535, inklusif.
  • Jenis int mewakili bilangan bulat 32-bit bertanda dengan nilai dari -2147483648 sampai 2147483647 inklusif.
  • uint tipe mewakili bilangan bulat 32-bit tanpa tanda dengan nilai dari 0 hingga 4294967295, termasuk.
  • Tipe long mewakili bilangan bulat 64-bit bertanda dengan nilai dari -9223372036854775808 sampai 9223372036854775807, inklusif.
  • Jenis ulong mewakili bilangan bulat 64-bit yang tidak bertanda dengan nilai dari 0 hingga 18446744073709551615, termasuk.
  • Jenis char mewakili bilangan bulat 16-bit tanpa tanda dengan nilai dari 0 hingga 65535, inklusif. Kumpulan nilai yang mungkin untuk tipe char sesuai dengan kumpulan karakter Unicode.

    Catatan: Meskipun char memiliki representasi yang sama dengan ushort, tidak semua operasi yang diizinkan pada satu jenis diizinkan di jenis lainnya. catatan akhir

Semua tipe bilangan bulat bertanda diwakili menggunakan format komplemen dua.

Operator integral_type unary dan biner selalu beroperasi dengan presisi 32-bit berpenanda, presisi 32-bit tanpa tanda, presisi 64-bit berpenanda, atau presisi 64-bit tanpa tanda, seperti yang dirinci dalam §12.4.7.

Jenis ini char diklasifikasikan sebagai jenis integral, tetapi berbeda dari jenis integral lainnya dengan dua cara:

  • Tidak ada konversi implisit yang telah ditentukan sebelumnya dari jenis lain ke jenisnya char . Secara khusus, meskipun byte dan ushort jenis memiliki rentang nilai yang sepenuhnya dapat diwakili menggunakan jenis char, konversi implisit dari sbyte, byte, atau ushort ke char tidak ada.
  • Konstanta jenis char harus ditulis sebagai character_literal atau sebagai integer_literal dalam kombinasi dengan casting ke tipe karakter.

Contoh: (char)10 sama '\x000A'dengan . contoh akhir

Operator dan pernyataan checked dan unchecked digunakan untuk mengontrol pemeriksaan overflow pada operasi aritmatika dan konversi tipe integral (§12.8.20). checked Dalam konteks, luapan menghasilkan kesalahan waktu kompilasi atau menyebabkan System.OverflowException dilemparkan. Dalam konteks unchecked, limpahan diabaikan dan bit orde tinggi apa pun yang tidak sesuai dengan tipe tujuan dibuang.

8.3.7 Jenis titik mengambang

C# mendukung dua jenis floating-point: float dan double. Jenis float dan double diwakili menggunakan format presisi tunggal 32-bit dan presisi ganda 64-bit IEC 60559, yang menyediakan set nilai berikut:

  • Nol positif dan nol negatif. Dalam kebanyakan situasi, nol positif dan nol negatif berperilaku identik sebagai nilai nol sederhana, tetapi operasi tertentu membedakan antara keduanya (§12.10.3).
  • Tak terhingga positif dan tak terhingga negatif. Infinities diproduksi oleh operasi seperti membabungkan angka bukan nol dengan nol.

    Contoh: 1.0 / 0.0 menghasilkan tak terbatas positif, dan –1.0 / 0.0 menghasilkan tak terbatas negatif. contoh akhir

  • Nilai Bukan Angka , sering disingkat NaN. NaN dihasilkan dari operasi floating-point yang tidak valid, seperti membagi nol dengan nol.
  • Kumpulan nilai bukan nol terbatas dari bentuk s × m × 2, di mana s adalah 1 atau −1, dan m dan e ditentukan oleh jenis titik mengambang tertentu: Untuk float, 0 <m< 2²⁴ dan −149 ≤ e ≤ 104, dan untuk double, 0 <m< 2⁵³ dan −1075 ≤ e ≤ 970. Angka floating-point yang didenormalisasi dianggap sebagai nilai bukan nol yang valid. C# tidak memerlukan atau melarang bahwa implementasi yang sesuai mendukung angka floating-point denormalisasi.

Jenis float dapat mewakili nilai mulai dari sekitar 1.5 × 10⁻⁴⁵ hingga 3.4 × 10³⁸ dengan presisi 7 digit.

Jenis ini double dapat mewakili nilai mulai dari sekitar 5,0 × 10⁻³²⁴ hingga 1,7 × 10³⁰⁸ dengan presisi 15-16 digit.

Jika salah satu operand operator biner adalah jenis floating-point, maka promosi numerik standar diterapkan, sebagaimana dirinci dalam §12.4.7, dan operasi dilakukan dengan float atau double presisi.

Operator floating-point, termasuk operator penugasan, tidak pernah menghasilkan pengecualian. Sebaliknya, dalam situasi luar biasa, operasi floating-point menghasilkan nol, tak terbatas, atau NaN, seperti yang dijelaskan di bawah ini:

  • Hasil operasi floating-point dibulatkan ke nilai terdekat yang dapat diwakili dalam format tujuan.
  • Jika besarnya hasil operasi floating-point terlalu kecil untuk format tujuan, hasil operasi menjadi nol positif atau nol negatif.
  • Jika besarnya hasil operasi floating-point terlalu besar untuk format tujuan, hasil operasi menjadi tak terbatas positif atau negatif.
  • Jika operasi floating-point tidak valid, hasil operasi menjadi NaN.
  • Jika satu atau kedua operan dari operasi floating-point adalah NaN, hasil operasi menjadi NaN.

Operasi floating-point mungkin dilakukan dengan presisi lebih tinggi daripada tipe hasil dari operasi tersebut. Untuk memaksa nilai tipe floating-point menjadi presisi yang tepat dari tipe tersebut, cast eksplisit (§12.9.7) dapat digunakan.

Contoh: Beberapa arsitektur perangkat keras mendukung jenis floating-point "extended" atau "long double" dengan rentang dan presisi yang lebih besar daripada jenisnya double , dan secara implisit melakukan semua operasi floating-point menggunakan jenis presisi yang lebih tinggi ini. Hanya dengan biaya yang berlebihan dalam performa, arsitektur perangkat keras tersebut dapat dibuat untuk melakukan operasi floating-point dengan presisi yang lebih sedikit , dan daripada memerlukan implementasi untuk kehilangan performa dan presisi, C# memungkinkan jenis presisi yang lebih tinggi untuk digunakan untuk semua operasi floating-point. Selain memberikan hasil yang lebih tepat, ini jarang memiliki efek terukur. Namun, dalam ekspresi formulir x * y / z, di mana perkalian menghasilkan hasil yang berada di luar double rentang, tetapi pembagian berikutnya membawa hasil sementara kembali ke dalam double rentang, fakta bahwa ekspresi dievaluasi dalam format rentang yang lebih tinggi dapat menyebabkan hasil terbatas diproduksi alih-alih tak terbatas. contoh akhir

8.3.8 Jenis Desimal

Jenisnya decimal adalah jenis data 128-bit yang cocok untuk perhitungan keuangan dan moneter. decimal jenis dapat mewakili nilai termasuk rentang dari setidaknya -7,9 × 10⁻²⁸ hingga 7,9 × 10²⁸, dengan setidaknya presisi 28 digit.

Sekumpulan nilai terbatas dari jenis decimal adalah bentuk (–1)v × c × 10⁻e, di mana tanda v adalah 0 atau 1, koefisien < diberikan oleh 0 ≤ cCmax, dan skala e sedemikian sehingga EmineEmax, di mana Cmax adalah setidaknya 1 × 10²⁸, Emin ≤ 0, dan Emax ≥ 28. Jenis decimal tidak harus mendukung nol bertanda, ketakterbatasan, atau NaN.

decimal direpresentasikan sebagai bilangan bulat yang diskalakan oleh kekuatan sepuluh. Untuk decimal yang memiliki nilai absolut kurang dari 1.0m, nilainya tepat hingga setidaknya tempat desimal ke-28. Untuk decimal dengan nilai absolut yang lebih besar dari atau sama dengan 1.0m, nilainya memiliki ketepatan setidaknya 28 digit. Bertentangan dengan float jenis data dan double , angka pecahan desimal seperti 0.1 dapat diwakili persis dalam representasi desimal. Dalam representasi float dan double, angka tersebut sering memiliki ekspansi biner yang tidak berakhir, membuat representasi tersebut lebih rentan terhadap kesalahan pembulatan.

Jika salah satu operan operator biner berjenis decimal maka promosi numerik standar diterapkan, sebagaimana dirinci dalam §12.4.7, dan operasi dilakukan dengan double presisi.

Hasil dari operasi pada nilai jenis decimal adalah yang dihasilkan dari menghitung hasil yang tepat (mempertahankan skala, seperti yang didefinisikan untuk setiap operator) dan kemudian dibulatkan agar sesuai dengan bentuk representasi. Hasil dibulatkan ke nilai terdekat yang dapat direpresentasikan, dan ketika hasilnya sama dekat dengan dua nilai yang dapat direpresentasikan, dibulatkan ke nilai yang digit paling tak berarti-nya adalah angka genap (ini dikenal sebagai "pembulatan bankir"). Artinya, hasilnya tepat sampai setidaknya angka desimal ke-28. Perhatikan bahwa pembulatan dapat menghasilkan nilai nol dari nilai yang bukan nol.

Jika operasi aritmatika decimal menghasilkan hasil yang besarnya terlalu besar untuk format decimal, System.OverflowException akan dilemparkan.

Tipe decimal ini memiliki presisi yang lebih besar tetapi mungkin memiliki rentang yang lebih kecil daripada jenis floating-point. Dengan demikian, konversi dari jenis floating-point ke decimal dapat menghasilkan pengecualian luapan, dan konversi dari decimal ke jenis floating-point dapat menyebabkan hilangnya presisi atau pengecualian luapan. Untuk alasan ini, tidak ada konversi implisit antara tipe floating-point dan decimal, dan tanpa cast eksplisit, kesalahan waktu kompilasi terjadi ketika floating-point dan decimal operan dicampurkan langsung ke dalam ekspresi yang sama.

8.3.9 Jenis Bool

Jenis bool mewakili nilai logis Boolean. Nilai jenis bool yang mungkin adalah true dan false. Representasi false dijelaskan dalam §8.3.3. Meskipun representasi true tidak ditentukan, itu akan berbeda dari false.

Tidak ada konversi standar antara bool dan jenis nilai lainnya. Secara khusus, bool jenisnya berbeda dan terpisah dari jenis integral, bool nilai tidak dapat digunakan sebagai ganti nilai integral, dan sebaliknya.

Catatan: Dalam bahasa C dan C++, nilai nol integral atau floating-point, atau pointer null dapat dikonversi ke nilai falseBoolean , dan nilai integral atau floating-point bukan nol, atau pointer non-null dapat dikonversi ke nilai trueBoolean . Dalam C#, konversi tersebut dicapai dengan secara eksplisit membandingkan nilai integral atau floating-point dengan nol, atau dengan secara eksplisit membandingkan referensi objek dengan null. catatan akhir

8.3.10 Jenis enumerasi

Jenis enumerasi adalah tipe yang berbeda dengan konstanta yang diberi nama. Setiap jenis enumerasi memiliki jenis yang mendasar, yang akan menjadi byte, , sbyte, short, ushortint, uint, long atau ulong. Sekumpulan nilai jenis enumerasi sama dengan kumpulan nilai dari jenis yang mendasar. Nilai jenis enumerasi tidak dibatasi hanya pada nilai konstanta yang dinamai. Jenis enumerasi didefinisikan melalui deklarasi enumerasi (§19,2).

8.3.11 Jenis tuple

Jenis tuple mewakili urutan nilai yang berurutan dengan panjang tetap, serta memiliki nama opsional dan tipe yang berbeda-beda. Jumlah elemen dalam jenis tuple disebut aritas-nya. Jenis tuple ditulis (T1 I1, ..., Tn In) dengan n ≥ 2, di mana pengidentifikasi I1...In memiliki nama elemen tuple yang opsional.

Sintaks ini adalah singkatan untuk tipe yang dibangun dengan tipe T1...Tn dari System.ValueTuple<...>, yang akan menjadi kumpulan struct generik yang mampu secara langsung mengekspresikan jenis tuple dengan aritas berapa pun antara dua hingga tujuh termasuk. Tidak perlu adanya System.ValueTuple<...> deklarasi yang secara langsung sesuai dengan aritas setiap tipe tuple dengan jumlah parameter tipe yang bersesuaian. Sebaliknya, tuple dengan aritas yang lebih besar dari tujuh diwakili oleh tipe struct generik System.ValueTuple<T1, ..., T7, TRest> yang selain elemen tuple memiliki bidang Rest yang berisi nilai berlapis dari elemen yang tersisa, menggunakan tipe lain System.ValueTuple<...>. Bersarang seperti itu dapat diamati dengan berbagai cara, misalnya melalui keberadaan bidang Rest . Jika hanya satu bidang tambahan yang diperlukan, jenis System.ValueTuple<T1> struct generik digunakan; jenis ini tidak dianggap sebagai jenis tuple itu sendiri. Jika diperlukan lebih dari tujuh bidang tambahan, System.ValueTuple<T1, ..., T7, TRest> digunakan secara rekursif.

Nama elemen dalam jenis tuple harus berbeda. Nama elemen tuple dari bentuk ItemX, di mana X adalah urutan digit desimal yang tidak dimulai dengan 0 dan dapat mewakili posisi elemen tuple, hanya diperbolehkan di posisi yang ditentukan oleh X.

Nama elemen opsional tidak diwakili dalam tipe ValueTuple<...>, dan tidak disimpan dalam representasi runtime nilai tuple. Konversi identitas (§10.2.2) ada di antara tuple dengan urutan jenis elemen yang dapat dikonversi identitas.

Operator new§12.8.17.2 tidak dapat diterapkan dengan sintaks new (T1, ..., Tn)jenis tuple . Nilai tuple dapat dibuat dari ekspresi tuple (§12.8.6), atau dengan menerapkan new operator secara langsung pada tipe yang dibangun dari ValueTuple<...>.

Elemen Tuple adalah bidang publik dengan nama Item1, Item2 dll., dan dapat diakses melalui akses anggota pada nilai tuple (§12.8.7). Selain itu, jika jenis tuple memiliki nama untuk elemen tertentu, nama tersebut dapat digunakan untuk mengakses elemen yang dimaksud.

Catatan: Bahkan ketika tuple besar diwakili dengan nilai bersarang System.ValueTuple<...>, setiap elemen tuple masih dapat diakses langsung dengan nama Item... yang sesuai dengan posisinya. catatan akhir

Contoh: Diberikan contoh-contoh berikut:

(int, string) pair1 = (1, "One");
(int, string word) pair2 = (2, "Two");
(int number, string word) pair3 = (3, "Three");
(int Item1, string Item2) pair4 = (4, "Four");
// Error: "Item" names do not match their position
(int Item2, string Item123) pair5 = (5, "Five");
(int, string) pair6 = new ValueTuple<int, string>(6, "Six");
ValueTuple<int, string> pair7 = (7, "Seven");
Console.WriteLine($"{pair2.Item1}, {pair2.Item2}, {pair2.word}");

Jenis tuple untuk pair1, pair2, dan pair3 semuanya valid, dengan nama untuk tidak, beberapa atau semua elemen jenis tuple.

Jenis tuple untuk pair4 valid karena nama Item1 dan Item2 cocok dengan posisinya, sedangkan jenis tuple untuk pair5 tidak diizinkan, karena nama Item2 dan Item123 tidak.

Deklarasi untuk pair6 dan pair7 menunjukkan bahwa tipe tuple dapat dipertukarkan dengan tipe yang dibangun dalam bentuk ValueTuple<...>, dan bahwa operator new diizinkan dengan sintaks yang terakhir.

Baris terakhir menunjukkan bahwa elemen tuple dapat diakses dengan nama Item yang sesuai dengan posisinya, serta dengan nama elemen tuple yang bersangkutan, jika ada dalam tipenya. contoh akhir

8.3.12 Jenis nilai yang dapat bernilai null

Tipe nilai nullable dapat mewakili semua nilai dari tipe dasarnya ditambah nilai null tambahan. Jenis nilai nullable ditulis T?, di mana T adalah jenis yang dasar. Sintaks ini singkatan untuk System.Nullable<T>, dan dua bentuk dapat digunakan secara bergantian.

Sebaliknya, jenis nilai yang tidak dapat diubah ke null adalah jenis nilai apa pun selain System.Nullable<T> dan singkatannya T? (untuk apa pun T), ditambah parameter jenis apa pun yang dibatasi menjadi jenis nilai yang tidak dapat diubah ke null (yaitu, parameter jenis apa pun dengan batasan jenis nilai (§15.2.5)). Jenis System.Nullable<T> menentukan batasan jenis nilai untuk T, yang berarti bahwa jenis dasar dari jenis nilai null dapat berupa jenis nilai yang non-nullable. Jenis dasar dari tipe nilai nullable tidak boleh berupa tipe nilai nullable atau tipe referensi. Misalnya, int?? adalah jenis yang tidak valid. Jenis referensi nullable tercakup dalam §8.9.

Sebuah instance dari tipe nilai yang dapat bernilai null memiliki dua properti publik yang hanya dapat dibaca:

  • Properti HasValue dari jenis bool
  • Properti Value dari jenis T

Instans yang HasValuetrue dikatakan non-null. Instans non-null berisi nilai yang diketahui dan Value mengembalikan nilai tersebut.

Instans yang HasValuefalse dikatakan null. Instans null memiliki nilai yang tidak ditentukan. Mencoba membaca Value instans null menyebabkan dilemparkan System.InvalidOperationException . Proses mengakses properti Value dari instance yang dapat bernilai null disebut sebagai unwrapping.

Selain konstruktor default, setiap tipe nilai nullable T? memiliki konstruktor publik dengan satu parameter bertipe T. Dengan nilai x bertipe T, pemanggilan konstruktor dalam bentuk

new T?(x)

membuat instans T? non-null yang Value propertinya adalah x. Proses pembuatan instans non-null dari jenis nilai nullable untuk nilai tertentu disebut sebagai pembungkus.

Konversi implisit tersedia dari null literal ke T? (§10.2.7) dan dari T ke T? (§10.2.6).

Jenis nilai nullable T? tidak mengimplementasikan antarmuka (§18). Secara khusus, ini berarti tidak mengimplementasikan antarmuka apa pun yang diterapkan oleh tipe dasar T.

8.3.13 Tinju dan pembukaan kotak

Konsep pembungkusan dan pembongkaran menyediakan jembatan antara value_type dan reference_type dengan mengizinkan nilai apa pun dari value_type untuk dikonversi ke dan dari jenis object. Boxing dan unboxing memungkinkan tampilan terpadu dari sistem tipe di mana nilai dari tipe apa pun pada akhirnya dapat diperlakukan sebagai object.

Tinju dijelaskan secara lebih rinci dalam §10.2.9 dan pembukaan kotak dijelaskan dalam §10.3.7.

8.4 Jenis konstruksi

8.4.1 Umum

Deklarasi jenis generik, dengan sendirinya, menunjukkan jenis generik yang tidak terikat yang digunakan sebagai "cetak biru" untuk membentuk berbagai jenis, dengan cara menerapkan argumen jenis. Argumen jenis ditulis dalam tanda kurung sudut (< dan >) segera mengikuti nama jenis generik. Jenis yang menyertakan setidaknya satu argumen jenis disebut jenis yang dibangun. Tipe yang dibangun dapat digunakan di sebagian besar tempat dalam bahasa di mana nama tipe dapat muncul. Jenis generik yang tidak terikat hanya dapat digunakan dalam typeof_expression (§12.8.18).

Jenis yang dibuat juga dapat digunakan dalam ekspresi sebagai nama sederhana (§12.8.4) atau saat mengakses anggota (§12.8.7).

Saat namespace_or_type_name dievaluasi, hanya jenis generik dengan jumlah parameter jenis yang benar yang dipertimbangkan. Dengan demikian, dimungkinkan untuk menggunakan pengidentifikasi yang sama untuk mengidentifikasi berbagai jenis, selama jenis memiliki jumlah parameter jenis yang berbeda. Ini berguna saat mencampur kelas generik dan non-generik dalam program yang sama.

Contoh:

namespace Widgets
{
    class Queue {...}
    class Queue<TElement> {...}
}

namespace MyApplication
{
    using Widgets;

    class X
    {
        Queue q1;      // Non-generic Widgets.Queue
        Queue<int> q2; // Generic Widgets.Queue
    }
}

contoh akhir

Aturan terperinci untuk pencarian nama dalam produksi namespace_or_type_name dijelaskan dalam §7,8. Resolusi ambiguitas dalam produksi ini dijelaskan dalam §6.2.5. type_name mungkin mengidentifikasi jenis yang dibangun meskipun tidak menentukan parameter jenis secara langsung. Ini dapat terjadi di mana tipe yang bersarang dalam deklarasi generik class, dan tipe instansi deklarasi yang berisi secara implisit digunakan untuk pencarian nama (§15.3.9.7).

Contoh:

class Outer<T>
{
    public class Inner {...}

    public Inner i; // Type of i is Outer<T>.Inner
}

contoh akhir

Jenis non-enum yang dibangun tidak boleh digunakan sebagai unmanaged_type (§8.8).

8.4.2 Tipe argumen

Setiap argumen dalam daftar argumen jenis hanyalah jenis.

type_argument_list
    : '<' type_argument (',' type_argument)* '>'
    ;

type_argument
    : type
    | type_parameter nullable_type_annotation?
    ;

Setiap argumen jenis harus memenuhi batasan apa pun pada parameter jenis yang sesuai (§15.2.5). Argumen jenis referensi yang nullability-nya tidak cocok dengan nullability parameter type memenuhi batasan; namun peringatan dapat dikeluarkan.

8.4.3 Jenis terbuka dan tertutup

Semua jenis dapat diklasifikasikan sebagai jenis terbuka atau jenis tertutup. Jenis terbuka adalah jenis yang melibatkan parameter jenis. Lebih spesifik:

  • Parameter tipe mendefinisikan tipe yang terbuka.
  • Jenis array adalah jenis terbuka jika dan hanya jika jenis elemennya adalah jenis terbuka.
  • Jenis yang dibangun adalah jenis terbuka jika dan hanya jika satu atau beberapa argumen jenisnya adalah jenis terbuka. Jenis bersarang yang terbangun merupakan jenis terbuka jika dan hanya jika salah satu atau lebih argumen jenisnya atau argumen jenis dari satu atau lebih jenis yang mengandungnya merupakan jenis terbuka.

Jenis tertutup adalah tipe yang bukan tipe terbuka.

Pada run-time, semua kode dalam deklarasi tipe generik dijalankan dalam konteks tipe konstruksi tertutup yang dibuat dengan menerapkan argumen tipe ke deklarasi generik. Setiap parameter tipe dalam tipe generik terikat ke tipe waktu-nyata tertentu. Pemrosesan run-time dari semua pernyataan dan ekspresi selalu terjadi dengan tipe tertutup, dan tipe terbuka hanya terjadi selama pemrosesan waktu kompilasi.

Dua tipe terkontruksi tertutup dapat dikonversi secara identitas (§10.2.2) jika dibangun dari tipe generik tak terikat yang sama, dan konversi identitas ada di antara setiap argumen tipe yang bersesuaian. Argumen jenis yang sesuai dapat berupa jenis konstruk atau tuple yang dibuat terbatas yang dapat dikonversikan identitasnya. Jenis konstruksi tertutup yang dapat dikonversi identitas berbagi satu set variabel statis. Jika tidak, setiap jenis yang dibangun tertutup memiliki sekumpulan variabel statisnya sendiri. Karena jenis terbuka tidak ada pada run-time, tidak ada variabel statis yang terkait dengan jenis terbuka.

8.4.4 Jenis terikat dan tidak terikat

Istilah jenis tidak terikat mengacu pada jenis non-generik atau jenis generik yang tidak terikat. Istilah jenis terikat mengacu pada jenis non-generik atau jenis yang dibangun.

Jenis yang tidak terikat mengacu pada entitas yang dideklarasikan oleh deklarasi jenis. Jenis generik yang tidak terikat bukan tipe, dan tidak dapat digunakan sebagai jenis variabel, argumen, atau nilai pengembalian, atau sebagai jenis dasar. Satu-satunya konstruksi di mana jenis generik yang tidak terikat dapat direferensikan adalah typeof ekspresi (§12.8.18).

8.4.5 Memenuhi batasan

Setiap kali jenis yang dibangun atau metode generik direferensikan, argumen jenis yang disediakan diperiksa terhadap batasan parameter jenis yang dideklarasikan pada jenis atau metode generik (§15.2.5). Untuk setiap where klausa, argumen A jenis yang sesuai dengan parameter jenis bernama diperiksa terhadap setiap batasan sebagai berikut:

  • Jika batasan adalah class jenis, jenis antarmuka, atau parameter jenis, biarkan C mewakili batasan tersebut dengan argumen jenis yang disediakan diganti untuk parameter jenis apa pun yang muncul dalam batasan. Untuk memenuhi pengekangan, harus ada kasus di mana jenis A dapat dikonversi menjadi tipe C melalui salah satu metode berikut:
    • Konversi identitas (§10.2.2)
    • Konversi rujukan implisit (§10.2.8)
    • Konversi tinju (§10.2.9), asalkan jenis A tersebut adalah jenis nilai yang tidak dapat diubah ke null.
    • Konversi perujukan implisit, parameter jenis, atau tinju dari parameter jenis A ke C.
  • Jika batasan adalah batasan jenis referensi (class), jenis A harus memenuhi salah satu hal berikut:
    • A adalah jenis antarmuka, jenis kelas, jenis delegasi, jenis array, atau jenis dinamis.

    Catatan: System.ValueType dan System.Enum merupakan jenis referensi yang memenuhi batasan ini. catatan akhir

    • A adalah parameter jenis yang dikenal sebagai jenis referensi (§8.2).
  • Jika batasan adalah batasan jenis nilai (struct), jenis A harus memenuhi salah satu hal berikut:
    • A adalah tipe struct atau tipe enum, tetapi bukan tipe nilai yang dapat bernilai null.

    Catatan: System.ValueType dan System.Enum merupakan jenis referensi yang tidak memenuhi batasan ini. catatan akhir

    • A adalah parameter jenis yang memiliki batasan jenis nilai (§15.2.5).
  • Jika batasan adalah batasan konstruktor new(), jenisnya A tidak boleh abstract dan harus memiliki konstruktor tanpa parameter publik. Ini terpenuhi jika salah satu hal berikut ini benar:
    • A adalah jenis nilai, karena semua jenis nilai memiliki konstruktor default publik (§8.3.3).
    • A adalah parameter jenis yang memiliki batasan konstruktor (§15.2.5).
    • A adalah parameter jenis yang memiliki batasan jenis nilai (§15.2.5).
    • A adalah class yang tidak abstrak dan berisi konstruktor publik yang dideklarasikan secara eksplisit tanpa parameter.
    • A tidak abstract dan memiliki konstruktor default (§15.11.5).

Kesalahan waktu kompilasi terjadi jika satu atau beberapa batasan parameter jenis tidak terpenuhi oleh argumen jenis yang diberikan.

Karena parameter jenis tidak diwariskan, batasan juga tidak pernah diwariskan.

Contoh: Dalam hal berikut, D perlu menentukan batasan pada parameter T jenisnya sehingga T memenuhi batasan yang diberlakukan oleh dasar classB<T>. Sebaliknya, classE tidak perlu menentukan batasan, karena List<T> mengimplementasikan IEnumerable untuk setiap T.

class B<T> where T: IEnumerable {...}
class D<T> : B<T> where T: IEnumerable {...}
class E<T> : B<List<T>> {...}

contoh akhir

8.5 Jenis parameter

Parameter jenis adalah pengidentifikasi yang menunjuk jenis nilai atau jenis referensi yang terikat parameter pada run-time.

type_parameter
    : identifier
    ;

Karena parameter jenis dapat dibuat dengan banyak argumen jenis yang berbeda, parameter jenis memiliki operasi dan pembatasan yang sedikit berbeda dari jenis lainnya.

Catatan: Ini termasuk:

  • Parameter jenis tidak dapat digunakan langsung untuk mendeklarasikan kelas dasar (§15.2.4.2) atau antarmuka (§18.2.4).
  • Aturan untuk pencarian anggota pada parameter jenis bergantung pada batasan, jika ada, diterapkan ke parameter jenis. Mereka dirinci dalam §12.5.
  • Konversi yang tersedia untuk parameter jenis bergantung pada batasan, jika ada, diterapkan ke parameter jenis. Mereka dirinci dalam §10.2.12 dan §10.3.8.
  • Literal null tidak dapat dikonversi ke jenis yang diberikan oleh parameter jenis, kecuali jika parameter jenis diketahui sebagai jenis referensi (§10.2.12). Namun, ekspresi default (§12.8.21) dapat digunakan sebagai gantinya. Selain itu, nilai dengan jenis yang diberikan oleh parameter jenis dapat dibandingkan dengan null menggunakan == dan != (§12.12.7) kecuali parameter jenis memiliki batasan jenis nilai.
  • Ekspresi (new) hanya dapat digunakan dengan parameter jenis jika parameter jenis dibatasi oleh constructor_constraint atau batasan jenis nilai (§15.2.5).
  • Parameter jenis tidak dapat digunakan di mana saja dalam atribut.
  • Parameter jenis tidak dapat digunakan dalam akses anggota (§12.8.7) atau nama jenis (§7,8) untuk mengidentifikasi anggota statis atau jenis berlapis.
  • Parameter jenis tidak dapat digunakan sebagai unmanaged_type (§8,8).

catatan akhir

Sebagai jenis, parameter jenis murni konstruksi waktu kompilasi. Selama waktu eksekusi, setiap parameter jenis diikat ke jenis yang berlaku selama waktu eksekusi, yang telah ditentukan dengan memberikan argumen jenis pada deklarasi tipe generik. Dengan demikian, jenis variabel yang dideklarasikan dengan parameter jenis akan, pada run-time, menjadi jenis konstruksi tertutup §8.4.3. Eksekusi waktu jalan semua pernyataan dan ekspresi yang melibatkan parameter tipe menggunakan tipe yang disediakan sebagai argumen tipe untuk parameter tersebut.

8.6 Jenis pohon ekspresi

Pohon ekspresi mengizinkan ekspresi lambda direpresentasikan sebagai struktur data alih-alih kode yang dapat dieksekusi. Pohon ekspresi adalah nilai dari jenis pohon ekspresi berbentuk System.Linq.Expressions.Expression<TDelegate>, di mana TDelegate adalah tipe delegasi apa pun. Untuk sisa spesifikasi ini, jenis ini akan dirujuk menggunakan singkatan Expression<TDelegate>.

Jika ada konversi dari ekspresi lambda ke jenis delegasi D, maka juga ada konversi ke jenis pohon ekspresi Expression<TDelegate>. Sedangkan konversi ekspresi lambda ke jenis delegasi menghasilkan delegasi yang mereferensikan kode yang dapat dieksekusi untuk ekspresi lambda, konversi ke jenis pohon ekspresi membuat representasi pohon ekspresi dari ekspresi lambda. Detail lebih lanjut dari konversi ini disediakan dalam §10.7.3.

Contoh: Program berikut mewakili ekspresi lambda baik sebagai kode yang dapat dieksekusi maupun sebagai pohon ekspresi. Karena konversi ada ke Func<int,int>, konversi juga ada ke Expression<Func<int,int>>:

Func<int,int> del = x => x + 1;             // Code
Expression<Func<int,int>> exp = x => x + 1; // Data

Setelah penugasan ini, delegasi del merujuk metode yang mengembalikan x + 1, dan pohon ekspresi exp merujuk struktur data yang menjelaskan ekspresi x => x + 1.

contoh akhir

Expression<TDelegate> menyediakan metode Compile instans yang menghasilkan delegasi jenis TDelegate:

Func<int,int> del2 = exp.Compile();

Memanggil delegasi ini menyebabkan kode yang diwakili oleh pohon ekspresi dijalankan. Dengan demikian, mengingat definisi di atas, del dan del2 setara, dan dua pernyataan berikut akan memiliki efek yang sama:

int i1 = del(1);
int i2 = del2(1);

Setelah menjalankan kode ini, i1 dan i2 keduanya akan memiliki nilai 2.

Permukaan API yang disediakan oleh Expression<TDelegate> ditentukan implementasi di luar persyaratan untuk metode yang Compile dijelaskan di atas.

Catatan: Meskipun detail API yang disediakan untuk pohon ekspresi ditentukan implementasi, diharapkan implementasi akan:

  • Mengaktifkan kode untuk memeriksa dan merespons struktur pohon ekspresi yang dibuat sebagai hasil konversi dari ekspresi lambda
  • Mengaktifkan pohon ekspresi untuk dibuat secara terprogram dalam kode pengguna

catatan akhir

8.7 Jenis dinamis

Jenis dynamic ini menggunakan pengikatan dinamis, seperti yang dijelaskan secara rinci dalam §12.3.2, dibandingkan dengan pengikatan statis yang digunakan oleh semua jenis lainnya.

Jenis dynamic ini dianggap identik dengan object kecuali dalam hal-hal berikut:

  • Operasi pada ekspresi jenis dynamic dapat terikat secara dinamis (§12.3.3).
  • Inferensi jenis (§12.6.3) akan lebih memilih dynamic daripada object jika keduanya adalah kandidat.
  • dynamic tidak dapat digunakan sebagai
    • jenis dalam object_creation_expression (§12.8.17.2)
    • class_base (§15.2.4)
    • predefined_type dalam member_access (§12.8.7.1)
    • operan dari operator typeof
    • argumen atribut
    • batasan
    • jenis metode ekstensi
    • setiap bagian dari argumen tipe dalam struct_interfaces (§16.2.5) atau interface_type_list (§15.2.4.1).

Karena kesetaraan ini, berikut ini berlaku:

  • Ada konversi identitas implisit
    • antara object dan dynamic
    • antara tipe yang dibangun yang identik ketika mengganti dynamic dengan object
    • antara jenis tuple yang sama saat mengganti dynamic dengan object
  • Konversi implisit dan eksplisit ke dan dari object juga berlaku ke dan dari dynamic.
  • Tanda tangan yang sama saat mengganti dynamic dengan object dianggap sebagai tanda tangan yang sama.
  • Jenis dynamic ini tidak dapat dibedakan dari jenis object pada run-time.
  • Ekspresi jenis dynamic disebut sebagai ekspresi dinamis.

8.8 Jenis tidak terkelola

unmanaged_type
    : value_type
    | pointer_type     // unsafe code support
    ;

unmanaged_type adalah jenis apa pun yang bukan reference_type atau type_parameter yang tidak dibatasi untuk tidak dikelola, dan tidak berisi bidang instans yang jenisnya bukan unmanaged_type. Dengan kata lain, unmanaged_type adalah salah satu hal berikut:

  • sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, atau bool.
  • Setiap enum_type.
  • Setiap struktur struct_type yang didefinisikan pengguna dan hanya berisikan bidang instance dari unmanaged_type.
  • Parameter jenis apa pun yang dibatasi untuk tidak dikelola.
  • Setiap pointer_type (§23.3).

8.9 Jenis Referensi dan penghilangan nilai

8.9.1 Umum

Tipe referensi yang dapat bernilai null diindikasikan dengan menambahkan nullable_type_annotation () ke tipe referensi yang tidak bernilai null. Tidak ada perbedaan semantik antara jenis referensi yang tidak dapat diubah ke null dan jenis nullable yang sesuai, keduanya dapat menjadi referensi ke objek atau null. Kehadiran atau tidak adanya nullable_type_annotation menyatakan apakah ekspresi dimaksudkan untuk mengizinkan nilai null atau tidak. Pengkompilasi dapat memberikan diagnostik ketika ekspresi tidak digunakan sesuai dengan niat tersebut. Status null ekspresi ditentukan dalam §8.9.5. Konversi identitas ada di antara jenis referensi nullable dan jenis referensi non-nullable yang bersesuaian (§10.2.2).

Ada dua bentuk kemungkinan bernilai null untuk tipe referensi.

  • nullable: Jenis referensi nullable dapat ditetapkan . Status null bawaan adalah mungkin-null.
  • tidak dapat bernilai null: Referensi yang tidak dapat bernilai null tidak boleh diberi null nilai. Status null bawaan adalah tidak null.

Catatan: Jenis R dan R? diwakili oleh jenis yang mendasar yang sama, R. Variabel dari jenis yang mendasar tersebut dapat berisi referensi ke objek atau menjadi nilai null, yang menunjukkan "tidak ada referensi." catatan akhir

Perbedaan sintaktik antara jenis referensi nullable dan jenis referensi non-nullable yang sesuai memungkinkan pengkompilasi untuk menghasilkan diagnostik. Pengkompilasi harus mengizinkan nullable_type_annotation seperti yang didefinisikan dalam §8.2.1. Diagnostik harus terbatas pada peringatan. Apakah ada atau tidaknya anotasi nullable, maupun status konteks nullable tidak dapat mengubah waktu kompilasi atau perilaku runtime program, kecuali pada perubahan pesan diagnostik yang dihasilkan pada waktu kompilasi.

8.9.2 Jenis referensi yang tidak dapat diubah ke null

Jenis referensi non-nullable adalah tipe referensi dalam bentuk T, di mana T adalah nama dari jenis tersebut. Status null default dari variabel yang tidak dapat bernilai null adalah tidak-null. Peringatan dapat dihasilkan ketika ekspresi yang mungkin-null digunakan di mana nilai bukan null diperlukan.

8.9.3 Jenis referensi nullable

Jenis referensi formulir (seperti T?) adalah jenisstring? null. Status null default dari variabel nullable adalah mungkin null. Anotasi ? menunjukkan niat bahwa variabel jenis ini dapat diubah ke null. Pengkompilasi dapat mengenali niat ini untuk mengeluarkan peringatan. Ketika konteks anotasi nullable dinonaktifkan, menggunakan anotasi ini dapat menghasilkan peringatan.

8.9.4 Konteks nullable

8.9.4.1 Umum

Setiap baris kode sumber memiliki konteks nullable. Anotasi dan tanda peringatan untuk kontrol konteks nullable dari anotasi nullable (§8.9.4.3) dan peringatan terkait nullable (§8.9.4.4), masing-masing. Setiap bendera dapat diaktifkan atau dinonaktifkan. Pengkompilasi dapat menggunakan analisis aliran statis untuk menentukan status null dari variabel referensi apa pun. Status null variabel referensi (§8.9.5) bukan null, mungkin null, atau mungkin default.

Konteks nullable dapat ditentukan dalam kode sumber melalui direktif nullable (§6.5.9) dan/atau melalui beberapa mekanisme khusus implementasi di luar kode sumber. Jika kedua pendekatan digunakan, arahan nullable menggantikan pengaturan yang dibuat melalui mekanisme eksternal.

Keadaan default dari konteks nullable ditentukan oleh implementasi.

Sepanjang spesifikasi ini, semua kode C# yang tidak berisi direktif nullable, atau tentang mana tidak ada pernyataan yang dibuat mengenai status konteks nullable saat ini, harus diasumsikan telah dikompilasi menggunakan konteks nullable di mana anotasi dan peringatan diaktifkan.

Catatan: Konteks nullable di mana kedua bendera dinonaktifkan cocok dengan perilaku standar sebelumnya untuk jenis referensi. catatan akhir

8.9.4.2 Nonaktifkan Nullable

Ketika bendera peringatan dan anotasi dinonaktifkan, konteks nullable dinonaktifkan.

Ketika konteks nullable dinonaktifkan:

  • Tidak ada peringatan yang akan dihasilkan ketika variabel dari jenis referensi yang tidak diannotasi diinisialisasi dengan, atau diberi nilai, null.
  • Tidak ada peringatan yang akan dihasilkan ketika variabel jenis referensi yang mungkin memiliki nilai null.
  • Untuk jenis Treferensi apa pun , anotasi ? dalam T? menghasilkan pesan dan jenisnya T? sama dengan T.
  • Untuk batasan where T : C?parameter jenis apa pun , anotasi ? dalam C? menghasilkan pesan dan jenisnya C? sama dengan C.
  • Untuk batasan where T : U?parameter jenis apa pun , anotasi ? dalam U? menghasilkan pesan dan jenisnya U? sama dengan U.
  • Batasan class? generik menghasilkan pesan peringatan. Parameter jenis harus berupa jenis referensi.

    Catatan: Pesan ini ditandai sebagai "informasi" daripada "peringatan," agar tidak membingungkannya dengan status pengaturan peringatan nullable, yang tidak terkait. catatan akhir

  • Operator ! pengampunan null (§12.8.9) tidak berpengaruh.

Contoh:

#nullable disable annotations
string? s1 = null;    // Informational message; ? is ignored
string s2 = null;     // OK; null initialization of a reference
s2 = null;            // OK; null assignment to a reference
char c1 = s2[1];      // OK; no warning on dereference of a possible null;
                      //     throws NullReferenceException
c1 = s2![1];          // OK; ! is ignored

contoh akhir

8.9.4.3 Anotasi nullable

Ketika bendera peringatan dinonaktifkan dan bendera anotasi diaktifkan, konteks nullable adalah anotasi.

Ketika konteks nullable adalah anotasi:

  • Untuk jenis referensi T apa pun, anotasi ? dalam T? menunjukkan bahwa T? adalah jenis yang dapat bernilai null, sedangkan yang tidak dianotasi T tidak dapat bernilai null.
  • Tidak ada peringatan diagnostik yang terkait dengan nullability yang dihasilkan.
  • Operator ! null-forgiving (§12.8.9) dapat mengubah status null operand yang dianalisis dan menentukan peringatan diagnostik apa yang muncul saat waktu kompilasi.

Contoh:

#nullable disable warnings
#nullable enable annotations
string? s1 = null;    // OK; ? makes s2 nullable
string s2 = null;     // OK; warnings are disabled
s2 = null;            // OK; warnings are disabled
char c1 = s2[1];      // OK; warnings are disabled; throws NullReferenceException
c1 = s2![1];          // No warnings

contoh akhir

8.9.4.4 Peringatan nullable

Ketika bendera peringatan diaktifkan dan bendera anotasi dinonaktifkan, konteks nullable adalah peringatan.

Ketika konteks nullable adalah peringatan, pengkompilasi dapat menghasilkan diagnostik dalam kasus berikut:

  • Variabel referensi yang telah ditentukan sebagai mungkin null telah didereferensi.
  • Variabel referensi dari jenis non-nullable diberikan ke ekspresi yang mungkin null.
  • ? digunakan untuk mencatat jenis referensi yang dapat diubah ke null.
  • Operator null-forgiving ! (§12.8.9) digunakan untuk mengatur status null operandnya menjadi tidak null.

Contoh:

#nullable disable annotations
#nullable enable warnings
string? s1 = null;    // OK; ? makes s2 nullable
string s2 = null;     // OK; null-state of s2 is "maybe null"
s2 = null;            // OK; null-state of s2 is "maybe null"
char c1 = s2[1];      // Warning; dereference of a possible null;
                      //          throws NullReferenceException
c1 = s2![1];          // The warning is suppressed

contoh akhir

8.9.4.5 Aktifkan Nullable

Ketika bendera peringatan dan bendera anotasi diaktifkan, konteks nullable diaktifkan.

Saat konteks nullable diaktifkan:

  • Untuk jenis Treferensi apa pun , anotasi ? dalam T? membuat T? jenis nullable, sedangkan yang tidak diannotasi T tidak dapat diubah ke null.
  • Pengkompilasi dapat menggunakan analisis aliran statis untuk menentukan status null dari variabel referensi apa pun. Ketika peringatan null diaktifkan, status null variabel referensi (§8.9.5) bukan null, mungkin null, atau mungkin default dan
  • Operator null-forgiving ! (§12.8.9) mengatur status null operand-nya menjadi tidak null.
  • Pengkompilasi dapat mengeluarkan peringatan, jika nullability dari parameter jenis tidak sesuai dengan nullability dari argumen jenis yang bersesuaian.

8.9.5 Ke-null-an dan status null

8.9.5.1 Umum

Pengkompilasi tidak diharuskan melakukan analisis statis, juga tidak diharuskan untuk menghasilkan peringatan diagnostik yang terkait dengan nullability.

Sisa subklasul ini bersifat normatif secara kondisional.

8.9.5.2 Analisis alur

Pengkompilasi yang menghasilkan peringatan diagnostik sesuai dengan aturan ini.

Setiap ekspresi memiliki salah satu dari tiga statusnull:

  • mungkin null: Nilai ekspresi mungkin bernilai null.
  • mungkin default: Nilai ekspresi dapat dievaluasi ke nilai default untuk jenis tersebut.
  • bukan null: Nilai ekspresi tidak null.

Status null default ekspresi ditentukan oleh jenisnya, dan status bendera anotasi saat dideklarasikan:

  • Status null default dari jenis referensi nullable adalah:
    • Mungkin null ketika deklarasinya ada di teks di mana penanda anotasi diaktifkan.
    • Tidak null ketika deklarasinya berada dalam teks di mana pengaturan anotasi dinonaktifkan.
  • Status null baku dari tipe referensi yang tidak dapat menerima null adalah bukan null.

Catatan: Status default mungkin digunakan dengan parameter jenis yang tidak dibatasi saat jenisnya adalah jenis yang tidak dapat diubah ke null, seperti string dan ekspresinya default(T) adalah nilai null. Karena null tidak ada di domain untuk jenis yang tidak dapat diubah ke null, statusnya mungkin default. catatan akhir

Diagnostik dapat diproduksi ketika variabel (§9.2.1) dari jenis referensi yang tidak dapat diubah ke null diinisialisasi atau ditetapkan ke ekspresi yang mungkin null ketika variabel tersebut dideklarasikan dalam teks tempat bendera anotasi diaktifkan.

Contoh: Pertimbangkan metode berikut di mana parameter dapat bernilai null dan nilai tersebut ditetapkan ke jenis yang tidak dapat bernilai null:

#nullable enable
public class C
{
    public void M(string? p)
    {
        // Warning: Assignment of maybe null value to non-nullable variable
        string s = p;
    }
}

Pengkompilasi dapat mengeluarkan peringatan di mana parameter yang mungkin null ditetapkan ke variabel yang seharusnya tidak null. Jika parameter diperiksa null sebelum penugasan, pengkompilasi dapat menggunakannya dalam analisis status nullable-nya dan tidak mengeluarkan peringatan:

#nullable enable
public class C
{
    public void M(string? p)
    {
        if (p != null)
        {
            string s = p; // No warning
            // Use s
        }
    }
}

contoh akhir

Pengkompilasi dapat memperbarui status null variabel sebagai bagian dari analisisnya.

Contoh: Pengkompilasi dapat memilih untuk memperbarui status berdasarkan pernyataan apa pun dalam program Anda:

#nullable enable
public void M(string? p)
{
    int length = p.Length; // Warning: p is maybe null

    string s = p; // No warning. p is not null

    if (s != null)
    {
        int l2 = s.Length; // No warning. s is not null
    }
    int l3 = s.Length; // Warning. s is maybe null
}

Dalam contoh sebelumnya, kompilator dapat memutuskan bahwa setelah pernyataan int length = p.Length;, status null p bukan null. Jika bernilai null, pernyataan itu akan menghasilkan NullReferenceException. Ini mirip dengan perilaku jika kode telah didahului oleh if (p == null) throw NullReferenceException(); kecuali bahwa kode seperti yang ditulis dapat menghasilkan peringatan, tujuannya adalah untuk memperingatkan bahwa pengecualian dapat dilemparkan secara implisit. contoh akhir

Kemudian dalam metode, kode memeriksa bahwa s bukan referensi null. Status null s dapat berubah menjadi mungkin null setelah blok yang diperiksa null ditutup. Pengkompilasi dapat menyimpulkan bahwa s mungkin null karena kode ditulis untuk mengasumsikan bahwa kode tersebut mungkin null. Umumnya, ketika kode berisi pemeriksaan null, pengkompilasi dapat menyimpulkan bahwa nilainya mungkin null:

Contoh: Setiap ekspresi berikut menyertakan beberapa bentuk pemeriksaan nilai null. Status null o dapat berubah dari tidak null menjadi mungkin null setelah setiap pernyataan ini:

#nullable enable
public void M(string s)
{
    int length = s.Length; // No warning. s is not null

    _ = s == null; // Null check by testing equality. The null state of s is maybe null
    length = s.Length; // Warning, and changes the null state of s to not null

    _ = s?.Length; // The ?. is a null check and changes the null state of s to maybe null
    if (s.Length > 4) // Warning. Changes null state of s to not null
    {
        _ = s?[4]; // ?[] is a null check and changes the null state of s to maybe null
        _ = s.Length; // Warning. s is maybe null
    }
}

Baik deklarasi properti otomatis maupun peristiwa yang menyerupai bidang memanfaatkan bidang dukungan yang dihasilkan oleh kompilator. Analisis status null dapat menyimpulkan bahwa penetapan pada event atau properti adalah penugasan ke bidang penyangga yang dihasilkan kompilator.

Contoh: Sebuah pengompilasi dapat menentukan bahwa ketika menulis properti otomatis atau peristiwa yang mirip bidang, itu menulis ke bidang penyimpanan yang dihasilkan oleh pengompilasi yang sesuai. Status null properti cocok dengan field pencadangan.

class Test
{
    public string P
    {
        get;
        set;
    }

    public Test() {} // Warning. "P" not set to a non-null value.

    static void Main()
    {
        var t = new Test();
        int len = t.P.Length; // No warning. Null state is not null.
    }
}

Dalam contoh sebelumnya, konstruktor tidak mengatur P ke nilai bukan null, dan pengkompilasi dapat mengeluarkan peringatan. Tidak ada peringatan ketika properti P diakses, karena jenis properti adalah jenis referensi yang tidak dapat diubah ke null. contoh akhir

Pengkompilasi dapat memperlakukan properti (§15,7) sebagai variabel dengan status, atau sebagai aksesor get dan set independen (§15,7,3).

Contoh: Kompilator dapat memilih apakah penulisan ke properti mengubah status null dari pembacaan properti, atau jika pembacaan properti tersebut mengubah status null dari properti itu.

class Test
{
    private string? _field;
    public string? DisappearingProperty
    {
        get
        {
            string tmp = _field;
            _field = null;
            return tmp;
        }
        set
        {
            _field = value;
        }
    }

    static void Main()
    {
        var t = new Test();
        if (t.DisappearingProperty != null)
        {
            int len = t.DisappearingProperty.Length; // No warning. A compiler can assume property is stateful
        }
    }
}

Dalam contoh sebelumnya, bidang pencadangan untuk DisappearingProperty diatur ke null saat dibaca. Namun, pengkompilasi dapat mengasumsikan bahwa membaca properti tidak mengubah status null ekspresi tersebut. contoh akhir

Pengkompilasi dapat menggunakan ekspresi apa pun yang mendereferensikan variabel, properti, atau peristiwa untuk mengatur status null ke bukan null. Jika bernilai null, ekspresi dereferensi akan memunculkan NullReferenceException:

Contoh:


public class C
{
    private C? child;

    public void M()
    {
        _ = child.child.child; // Warning. Dereference possible null value
        var greatGrandChild = child.child.child; // No warning. 
    }
}

contoh akhir

8.9.5.3 Konversi tipe

Pengkompilasi yang menghasilkan peringatan diagnostik sesuai dengan aturan ini.

Nota: Perbedaan dalam anotasi nullability tingkat atas atau berlapis dalam jenis tidak memengaruhi apakah konversi antara jenis diizinkan, karena tidak ada perbedaan semantik antara jenis referensi yang tidak dapat diubah ke null dan jenis nullable yang sesuai (§8.9.1). catatan akhir

Compiler dapat mengeluarkan peringatan ketika anotasi nullability berbeda antara dua tipe, baik tipe tingkat atas maupun tipe yang terbenam, ketika terjadi konversi yang mempersempit.

Contoh: Jenis yang berbeda dalam anotasi tingkat atas

#nullable enable
public class C
{
    public void M1(string p)
    {
        _ = (string?)p; // No warning, widening
    }

    public void M2(string? p)
    {
        _ = (string)p; // Warning, narrowing
        _ = (string)p!; // No warning, suppressed
    }
}

contoh akhir

Contoh: Jenis yang berbeda dalam anotasi nullability berlapis

#nullable enable
public class C
{
    public void M1((string, string) p)
    {
        _ = ((string?, string?))p; // No warning, widening
    }

    public void M2((string?, string?) p)
    {
        _ = ((string, string))p; // Warning, narrowing
        _ = ((string, string))p!; // No warning, suppressed
    }
}

contoh akhir

Pengkompilasi dapat mengikuti aturan untuk varian antarmuka (§18.2.3.3), mendelegasikan varians (§20,4), dan kovarian array (§17,6) dalam menentukan apakah akan mengeluarkan peringatan untuk konversi jenis.

#nullable enable
public class C
{
    public void M1(IEnumerable<string> p)
    {
        IEnumerable<string?> v1 = p; // No warning
    }

    public void M2(IEnumerable<string?> p)
    {
        IEnumerable<string> v1 = p; // Warning
        IEnumerable<string> v2 = p!; // No warning
    }

    public void M3(Action<string?> p)
    {
        Action<string> v1 = p; // No warning
    }

    public void M4(Action<string> p)
    {
        Action<string?> v1 = p; // Warning
        Action<string?> v2 = p!; // No warning
    }

    public void M5(string[] p)
    {
        string?[] v1 = p; // No warning
    }

    public void M6(string?[] p)
    {
        string[] v1 = p; // Warning
        string[] v2 = p!; // No warning
    }
}

contoh akhir

Pengkompilasi dapat mengeluarkan peringatan ketika nullability berbeda dalam salah satu arah dalam jenis yang tidak mengizinkan konversi varian.

#nullable enable
public class C
{
    public void M1(List<string> p)
    {
        List<string?> v1 = p; // Warning
        List<string?> v2 = p!; // No warning
    }

    public void M2(List<string?> p)
    {
        List<string> v1 = p; // Warning
        List<string> v2 = p!; // No warning
    }
}

contoh akhir

Akhir dari teks yang bersifat normatif kondisional