Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
7.1 Pengaktifan aplikasi
Program dapat dikompilasi baik sebagai pustaka kelas untuk digunakan sebagai bagian dari aplikasi lain, atau sebagai aplikasi yang mungkin dimulai secara langsung. Mekanisme untuk menentukan mode kompilasi ini ditentukan implementasi dan eksternal untuk spesifikasi ini.
Program yang dikompilasi sebagai aplikasi harus berisi setidaknya satu metode yang memenuhi syarat sebagai titik masuk dengan memenuhi persyaratan berikut:
- Ini akan memiliki nama
Main
. - Ini akan menjadi
static
. - Ini tidak akan generik.
- Ini harus dinyatakan dalam jenis non-generik. Jika jenis yang menyatakan metode adalah jenis berlapis, tidak ada jenis penutupnya yang mungkin umum.
- Ini mungkin memiliki pengubah
async
yang disediakan jenis pengembalian metode adalahSystem.Threading.Tasks.Task
atauSystem.Threading.Tasks.Task<int>
. - Jenis pengembaliannya adalah
void
,int
,System.Threading.Tasks.Task
, atauSystem.Threading.Tasks.Task<int>
. - Ini tidak boleh menjadi metode parsial (§15.6.9) tanpa implementasi.
- Daftar parameter harus kosong, atau memiliki parameter nilai tunggal dari jenis
string[]
.
Catatan: Metode dengan pengubah
async
harus memiliki salah satu dari dua jenis pengembalian yang ditentukan di atas untuk memenuhi syarat sebagai titik masuk. Metodeasync void
, atau metode yangasync
mengembalikan jenis yang dapat ditunggu yang berbeda sepertiValueTask
atauValueTask<int>
tidak memenuhi syarat sebagai titik masuk. catatan akhir
Jika lebih dari satu metode yang memenuhi syarat sebagai titik masuk dideklarasikan dalam program, mekanisme eksternal dapat digunakan untuk menentukan metode mana yang dianggap sebagai titik masuk aktual untuk aplikasi. Jika metode yang memenuhi syarat memiliki jenis int
pengembalian atau void
ditemukan, metode yang memenuhi syarat memiliki jenis System.Threading.Tasks.Task
pengembalian atau System.Threading.Tasks.Task<int>
tidak dianggap sebagai metode titik masuk. Ini adalah kesalahan waktu kompilasi untuk program yang akan dikompilasi sebagai aplikasi tanpa tepat satu titik masuk. Program yang dikompilasi sebagai pustaka kelas mungkin berisi metode yang akan memenuhi syarat sebagai titik masuk aplikasi, tetapi pustaka yang dihasilkan tidak memiliki titik masuk.
Biasanya, aksesibilitas yang dinyatakan (§7.5.2) dari metode ditentukan oleh pengubah akses (§15.3.6) yang ditentukan dalam deklarasinya, dan demikian pula aksesibilitas jenis yang dinyatakan ditentukan oleh pengubah akses yang ditentukan dalam deklarasinya. Agar metode tertentu dari jenis tertentu dapat dipanggil, jenis dan anggota harus dapat diakses. Namun, titik masuk aplikasi adalah kasus khusus. Secara khusus, lingkungan eksekusi dapat mengakses titik masuk aplikasi terlepas dari aksesibilitasnya yang dinyatakan dan terlepas dari aksesibilitas yang dinyatakan dari deklarasi jenis penutupnya.
Ketika metode titik masuk memiliki jenis pengembalian System.Threading.Tasks.Task
atau System.Threading.Tasks.Task<int>
, pengompilasi harus mensintesis metode titik masuk sinkron yang memanggil metode Main
yang sesuai. Metode yang disintesis memiliki parameter dan jenis pengembalian berdasarkan Main
metode :
- Daftar parameter metode yang disintesis sama dengan daftar
Main
parameter metode - Jika jenis
Main
pengembalian metode adalahSystem.Threading.Tasks.Task
, jenis pengembalian metode yang disintesis adalahvoid
- Jika jenis
Main
pengembalian metode adalahSystem.Threading.Tasks.Task<int>
, jenis pengembalian metode yang disintesis adalahint
Eksekusi metode yang disintesis berlanjut sebagai berikut:
- Metode yang disintesis memanggil
Main
metode , meneruskan nilai parameternyastring[]
sebagai argumen jika metode memiliki parameter seperti ituMain
. -
Main
Jika metode melemparkan pengecualian, pengecualian disebarluaskan oleh metode yang disintesis. - Jika tidak, titik masuk yang disintesis menunggu tugas yang dikembalikan selesai, memanggil
GetAwaiter().GetResult()
tugas, menggunakan metode instans tanpa parameter atau metode ekstensi yang dijelaskan oleh §C.3. Jika tugas gagal,GetResult()
akan melemparkan pengecualian, dan pengecualian ini disebarluaskan oleh metode yang disintesis. -
Main
Untuk metode dengan jenisSystem.Threading.Tasks.Task<int>
pengembalian , jika tugas berhasil diselesaikan,int
nilai yang dikembalikan olehGetResult()
dikembalikan dari metode yang disintesis.
Titik masuk aplikasi yang efektif adalah titik masuk yang dideklarasikan dalam program, atau metode yang disintesis jika diperlukan seperti yang dijelaskan di atas. Jenis pengembalian dari titik masuk yang efektif oleh karena itu selalu void
atau int
.
Saat aplikasi dijalankan, domain aplikasi baru dibuat. Beberapa instansiasi aplikasi yang berbeda mungkin ada pada komputer yang sama pada saat yang sama, dan masing-masing memiliki domain aplikasinya sendiri. Domain aplikasi memungkinkan isolasi aplikasi dengan bertindak sebagai kontainer untuk status aplikasi. Domain aplikasi bertindak sebagai kontainer dan batas untuk jenis yang ditentukan dalam aplikasi dan pustaka kelas yang digunakannya. Jenis yang dimuat ke dalam satu domain aplikasi berbeda dari jenis yang sama yang dimuat ke domain aplikasi lain, dan instans objek tidak dibagikan secara langsung antara domain aplikasi. Misalnya, setiap domain aplikasi memiliki salinan variabel statisnya sendiri untuk jenis ini, dan konstruktor statis untuk jenis dijalankan paling banyak sekali per domain aplikasi. Implementasi gratis untuk memberikan kebijakan atau mekanisme yang ditentukan implementasi untuk pembuatan dan penghancuran domain aplikasi.
Startup aplikasi terjadi ketika lingkungan eksekusi memanggil titik masuk efektif aplikasi. Jika titik masuk yang efektif mendeklarasikan parameter, maka selama startup aplikasi, implementasi harus memastikan bahwa nilai awal parameter tersebut adalah referensi non-null ke array string. Array ini akan terdiri dari referensi non-null ke string, yang disebut parameter aplikasi, yang diberikan nilai yang ditentukan implementasi oleh lingkungan host sebelum pengaktifan aplikasi. Tujuannya adalah untuk menyediakan informasi aplikasi yang ditentukan sebelum startup aplikasi dari tempat lain di lingkungan yang dihosting.
Catatan: Pada sistem yang mendukung baris perintah, parameter aplikasi sesuai dengan apa yang umumnya dikenal sebagai argumen baris perintah. catatan akhir
Jika jenis pengembalian titik masuk yang efektif adalah int
, nilai pengembalian dari pemanggilan metode oleh lingkungan eksekusi digunakan dalam penghentian aplikasi (§7,2).
Selain situasi yang tercantum di atas, metode titik masuk bertingkah seperti yang bukan titik masuk dalam setiap hal. Secara khusus, jika titik masuk dipanggil pada titik lain selama masa pakai aplikasi, seperti dengan pemanggilan metode reguler, tidak ada penanganan khusus dari metode: jika ada parameter, itu mungkin memiliki nilai null
awal , atau non-nilainull
yang mengacu pada array yang berisi referensi null. Demikian juga, nilai pengembalian titik masuk tidak memiliki signifikansi khusus selain dalam pemanggilan dari lingkungan eksekusi.
7.2 Penghentian aplikasi
Penghentian aplikasi mengembalikan kontrol ke lingkungan eksekusi.
Jika jenis pengembalian metode titik masuk efektif aplikasi adalah int
dan eksekusi selesai tanpa menghasilkan pengecualian, nilai yang int
dikembalikan berfungsi sebagai kode status penghentian aplikasi. Tujuan dari kode ini adalah untuk memungkinkan komunikasi keberhasilan atau kegagalan ke lingkungan eksekusi. Jika jenis pengembalian dari metode titik masuk yang efektif adalah void
dan eksekusi selesai tanpa menghasilkan pengecualian, kode status penghentian adalah 0
.
Jika metode titik masuk yang efektif berakhir karena pengecualian (§21.4), kode keluar ditentukan implementasi. Selain itu, implementasi dapat menyediakan API alternatif untuk menentukan kode keluar.
Apakah finalizer (§15.13) dijalankan sebagai bagian dari penghentian aplikasi ditentukan implementasi.
Catatan: Implementasi .NET Framework membuat setiap upaya yang wajar untuk memanggil finalizer (§15.13) untuk semua objeknya yang belum dikumpulkan sampah, kecuali pembersihan tersebut telah ditekan (dengan panggilan ke metode
GC.SuppressFinalize
pustaka , misalnya). catatan akhir
7.3 Deklarasi
Deklarasi dalam program C# mendefinisikan elemen konstituen program. Program C# diatur menggunakan namespace layanan. Ini diperkenalkan menggunakan deklarasi namespace (§14), yang dapat berisi deklarasi jenis dan deklarasi namespace berlapis. Deklarasi jenis (§14,7) digunakan untuk menentukan kelas (§15), struktur (§16), antarmuka (§18), enum (§19), dan delegasi (§20). Jenis anggota yang diizinkan dalam deklarasi jenis tergantung pada bentuk deklarasi jenis. Misalnya, deklarasi kelas dapat berisi deklarasi untuk konstanta (§15,4), bidang (§15,5), metode (§15,6), properti (§15,7), peristiwa (§15,8), pengindeks (§15,7), pengindeks (§15.7) 9), operator (§15,10), konstruktor instans (§15,11), konstruktor statis (§15,12), finalizer (§15,13), dan jenis berlapis (§15.3.9).
Deklarasi mendefinisikan nama dalam ruang deklarasi tempat deklarasi berada. Ini adalah kesalahan waktu kompilasi untuk memiliki dua deklarasi atau lebih yang memperkenalkan anggota dengan nama yang sama dalam ruang deklarasi, kecuali dalam kasus berikut:
- Dua deklarasi namespace layanan atau lebih dengan nama yang sama diizinkan dalam ruang deklarasi yang sama. Deklarasi namespace tersebut dikumpulkan untuk membentuk namespace logis tunggal dan berbagi satu ruang deklarasi.
- Deklarasi dalam program terpisah tetapi dalam ruang deklarasi namespace yang sama diizinkan untuk berbagi nama yang sama.
Catatan: Namun, deklarasi ini dapat memperkenalkan ambiguitas jika disertakan dalam aplikasi yang sama. catatan akhir
- Dua metode atau lebih dengan nama yang sama tetapi tanda tangan yang berbeda diizinkan dalam ruang deklarasi yang sama (§7,6).
- Dua atau lebih deklarasi jenis dengan nama yang sama tetapi jumlah parameter jenis yang berbeda diizinkan dalam ruang deklarasi yang sama (§7.8.2).
- Dua atau lebih deklarasi jenis dengan pengubah parsial dalam ruang deklarasi yang sama dapat memiliki nama yang sama, jumlah parameter jenis yang sama dan klasifikasi yang sama (kelas, struktur, atau antarmuka). Dalam hal ini, deklarasi jenis berkontribusi pada satu jenis dan diagregasi untuk membentuk satu ruang deklarasi (§15.2.7).
- Deklarasi namespace dan deklarasi jenis dalam ruang deklarasi yang sama dapat berbagi nama yang sama selama deklarasi jenis memiliki setidaknya satu parameter jenis (§7.8.2).
Ada beberapa jenis ruang deklarasi yang berbeda, seperti yang dijelaskan dalam hal berikut.
- Dalam semua unit kompilasi program, namespace_member_declaration tanpa namespace_declaration yang tertutup adalah anggota dari satu ruang deklarasi gabungan yang disebut ruang deklarasiglobal.
- Dalam semua unit kompilasi program, namespace_member_declarationdalam namespace_declarationyang memiliki nama namespace yang sepenuhnya memenuhi syarat yang sama adalah anggota dari satu ruang deklarasi gabungan.
- Setiap compilation_unit dan namespace_body memiliki ruang deklarasi alias. Setiap extern_alias_directive dan using_alias_directivecompilation_unit atau namespace_body menyumbangkan anggota ke ruang deklarasi alias (§14.5.2).
- Setiap kelas, struktur, atau deklarasi antarmuka non-parsial membuat ruang deklarasi baru. Setiap kelas parsial, struktur, atau deklarasi antarmuka berkontribusi pada ruang deklarasi yang dibagikan oleh semua bagian yang cocok dalam program yang sama (§16.2.4). Nama diperkenalkan ke dalam ruang deklarasi ini melalui class_member_declaration, struct_member_declaration, interface_member_declaration, atau type_parameter. Kecuali untuk deklarasi konstruktor instans yang kelebihan beban dan deklarasi konstruktor statis, kelas atau struktur tidak boleh berisi deklarasi anggota dengan nama yang sama dengan kelas atau struktur. Kelas, struktur, atau antarmuka mengizinkan deklarasi metode dan pengindeks yang kelebihan beban. Selain itu, kelas atau struktur mengizinkan deklarasi konstruktor dan operator instans yang kelebihan beban. Misalnya, kelas, struktur, atau antarmuka dapat berisi beberapa deklarasi metode dengan nama yang sama, asalkan deklarasi metode ini berbeda dalam tanda tangan mereka (§7,6). Perhatikan bahwa kelas dasar tidak berkontribusi pada ruang deklarasi kelas, dan antarmuka dasar tidak berkontribusi pada ruang deklarasi antarmuka. Dengan demikian, kelas atau antarmuka turunan diizinkan untuk mendeklarasikan anggota dengan nama yang sama dengan anggota yang diwariskan. Anggota seperti itu dikatakan menyembunyikan anggota yang diwariskan.
- Setiap deklarasi delegasi membuat ruang deklarasi baru. Nama diperkenalkan ke dalam ruang deklarasi ini melalui parameter (fixed_parameter dan parameter_array s) dan type_parameter.
- Setiap deklarasi enumerasi membuat ruang deklarasi baru. Nama diperkenalkan ke dalam ruang deklarasi ini melalui enum_member_declarations.
- Setiap deklarasi metode, deklarasi properti, deklarasi aksesor properti, deklarasi pengindeks, deklarasi aksesor pengindeks, deklarasi operator, deklarasi konstruktor instans, fungsi anonim, dan fungsi lokal menciptakan ruang deklarasi baru yang disebut ruang deklarasi variabel lokal. Nama diperkenalkan ke dalam ruang deklarasi ini melalui parameter (fixed_parameter dan parameter_array s) dan type_parameter. Aksesor set untuk properti atau pengindeks memperkenalkan nama
value
sebagai parameter. Isi anggota fungsi, fungsi anonim, atau fungsi lokal, jika ada, dianggap berlapis dalam ruang deklarasi variabel lokal. Ketika ruang deklarasi variabel lokal dan ruang deklarasi variabel lokal berlapis berisi elemen dengan nama yang sama, dalam cakupan nama lokal berlapis, nama lokal luar disembunyikan (§7.7.1) dengan nama lokal berlapis. - Ruang deklarasi variabel lokal tambahan dapat terjadi dalam deklarasi anggota, fungsi anonim, dan fungsi lokal. Nama diperkenalkan ke dalam ruang deklarasi ini melalui pola, declaration_expression, declaration_statementdan exception_specifier. Ruang deklarasi variabel lokal dapat ditumpuk, tetapi merupakan kesalahan untuk ruang deklarasi variabel lokal dan ruang deklarasi variabel lokal berlapis untuk berisi elemen dengan nama yang sama. Dengan demikian, dalam ruang deklarasi berlapis tidak dimungkinkan untuk mendeklarasikan variabel lokal, fungsi lokal atau konstanta dengan nama yang sama dengan parameter, parameter jenis, variabel lokal, fungsi lokal, atau konstanta dalam ruang deklarasi penutup. Dimungkinkan bagi dua ruang deklarasi untuk menampung elemen dengan nama yang sama selama tidak satu pun ruang deklarasi tersebut menampung satu sama lain. Ruang deklarasi lokal dibuat oleh konstruksi berikut:
- Setiap variable_initializer dalam deklarasi bidang dan properti memperkenalkan ruang deklarasi variabel lokalnya sendiri, yang tidak disarangkan dalam ruang deklarasi variabel lokal lainnya.
- Isi anggota fungsi, fungsi anonim, atau fungsi lokal, jika ada, membuat ruang deklarasi variabel lokal yang dianggap berlapis dalam ruang deklarasi variabel lokal fungsi.
- Setiap constructor_initializer membuat ruang deklarasi variabel lokal yang berlapis dalam deklarasi konstruktor instans. Ruang deklarasi variabel lokal untuk isi konstruktor pada gilirannya bersarang dalam ruang deklarasi variabel lokal ini.
- Setiap blok, switch_block, specific_catch_clause, iteration_statement, dan using_statement membuat ruang deklarasi variabel lokal berlapis.
- Setiap embedded_statement yang bukan bagian langsung dari statement_list membuat ruang deklarasi variabel lokal berlapis.
- Setiap switch_section membuat ruang deklarasi variabel lokal berlapis. Namun, variabel yang dideklarasikan langsung dalam statement_list switch_section (tetapi tidak dalam ruang deklarasi variabel lokal berlapis di dalam statement_list) ditambahkan langsung ke ruang deklarasi variabel lokal dari switch_block yang tertutup, bukan switch_section.
- Terjemahan syntactic dari query_expression (§12.20.3) dapat memperkenalkan satu atau beberapa ekspresi lambda. Sebagai fungsi anonim, masing-masing membuat ruang deklarasi variabel lokal seperti yang dijelaskan di atas.
- Setiap blok atau switch_block membuat ruang deklarasi terpisah untuk label. Nama diperkenalkan ke dalam ruang deklarasi ini melalui labeled_statement, dan nama direferensikan melalui goto_statement. Ruang deklarasi label blok mencakup blok berlapis. Dengan demikian, dalam blok berlapis tidak dimungkinkan untuk mendeklarasikan label dengan nama yang sama dengan label dalam blok penutup.
Catatan: Fakta bahwa variabel yang dideklarasikan langsung dalam switch_section ditambahkan ke ruang deklarasi variabel lokal switch_block alih-alih switch_section dapat menyebabkan kode yang mengejutkan. Dalam contoh di bawah ini, variabel
y
lokal berada dalam cakupan dalam bagian pengalihan untuk kasus default, meskipun deklarasi muncul di bagian sakelar untuk kasus 0. Variabelz
lokal tidak berada dalam cakupan dalam bagian pengalihan untuk kasus default, karena diperkenalkan dalam ruang deklarasi variabel lokal untuk bagian sakelar tempat deklarasi terjadi.int x = 1; switch (x) { case 0: int y; break; case var z when z < 10: break; default: y = 10; // Valid: y is in scope Console.WriteLine(x + y); // Invalid: z is not scope Console.WriteLine(x + z); break; }
catatan akhir
Urutan tekstual di mana nama dinyatakan umumnya tidak signifikan. Secara khusus, urutan tekstual tidak signifikan untuk deklarasi dan penggunaan namespace, konstanta, metode, properti, peristiwa, pengindeks, operator, konstruktor instans, finalizer, konstruktor statis, dan jenis. Urutan deklarasi signifikan dengan cara berikut:
- Urutan deklarasi untuk deklarasi bidang menentukan urutan penginisialisasinya (jika ada) dijalankan (§15.5.6.2, §15.5.6.3).
- Variabel lokal harus didefinisikan sebelum digunakan (§7,7).
- Urutan deklarasi untuk deklarasi anggota enum (§19,4) signifikan ketika nilai constant_expression dihilangkan.
Contoh: Ruang deklarasi namespace layanan "terbuka berakhir", dan dua deklarasi namespace dengan nama yang sepenuhnya memenuhi syarat yang sama berkontribusi pada ruang deklarasi yang sama. Misalnya
namespace Megacorp.Data { class Customer { ... } } namespace Megacorp.Data { class Order { ... } }
Dua deklarasi namespace di atas berkontribusi pada ruang deklarasi yang sama, dalam hal ini mendeklarasikan dua kelas dengan nama
Megacorp.Data.Customer
yang sepenuhnya memenuhi syarat danMegacorp.Data.Order
. Karena kedua deklarasi berkontribusi pada ruang deklarasi yang sama, itu akan menyebabkan kesalahan waktu kompilasi jika masing-masing berisi deklarasi kelas dengan nama yang sama.contoh akhir
Catatan: Seperti yang ditentukan di atas, ruang deklarasi blok menyertakan blok berlapis apa pun. Dengan demikian, dalam contoh berikut,
F
metode danG
mengakibatkan kesalahan waktu kompilasi karena namai
dideklarasikan di blok luar dan tidak dapat dideklarasikan ulang di blok dalam. Namun,H
metode danI
berlaku karena keduanyai
dinyatakan dalam blok terpisah yang tidak berlapis.class A { void F() { int i = 0; if (true) { int i = 1; } } void G() { if (true) { int i = 0; } int i = 1; } void H() { if (true) { int i = 0; } if (true) { int i = 1; } } void I() { for (int i = 0; i < 10; i++) { H(); } for (int i = 0; i < 10; i++) { H(); } } }
catatan akhir
7.4 Anggota
7.4.1 Umum
Namespace dan jenis memiliki anggota.
Catatan: Anggota entitas umumnya tersedia melalui penggunaan nama yang memenuhi syarat yang dimulai dengan referensi ke entitas, diikuti dengan token ""
.
, diikuti dengan nama anggota. catatan akhir
Anggota jenis dideklarasikan dalam deklarasi jenis atau diwariskan dari kelas dasar jenis. Ketika jenis mewarisi dari kelas dasar, semua anggota kelas dasar, kecuali konstruktor instans, finalizer, dan konstruktor statis menjadi anggota dari jenis turunan. Aksesibilitas yang dinyatakan dari anggota kelas dasar tidak mengontrol apakah anggota diwariskan—warisan meluas ke anggota mana pun yang bukan konstruktor instans, konstruktor statis, atau finalizer.
Catatan: Namun, anggota yang diwariskan mungkin tidak dapat diakses dalam jenis turunan, misalnya karena aksesibilitasnya yang dinyatakan (§7.5.2). catatan akhir
7.4.2 Anggota namespace
Namespace layanan dan jenis yang tidak memiliki namespace layanan penutup adalah anggota namespace layanan global. Ini sesuai langsung dengan nama yang dideklarasikan dalam ruang deklarasi global.
Namespace dan jenis yang dideklarasikan dalam namespace adalah anggota namespace tersebut. Ini sesuai langsung dengan nama yang dideklarasikan dalam ruang deklarasi namespace layanan.
Namespace layanan tidak memiliki batasan akses. Tidak dimungkinkan untuk mendeklarasikan namespace privat, dilindungi, atau internal, dan nama namespace selalu dapat diakses publik.
7.4.3 Anggota struct
Anggota struktur adalah anggota yang dinyatakan dalam struktur dan anggota yang diwarisi dari kelas System.ValueType
dasar langsung struct dan kelas object
dasar tidak langsung .
Anggota jenis sederhana sesuai langsung dengan anggota jenis struct yang di-alias oleh jenis sederhana (§8.3.5).
7.4.4 Anggota enumerasi
Anggota enumerasi adalah konstanta yang dideklarasikan dalam enumerasi dan anggota yang diwarisi dari kelas System.Enum
dasar langsung enumerasi dan kelas System.ValueType
dasar tidak langsung dan object
.
7.4.5 Anggota kelas
Anggota kelas adalah anggota yang dideklarasikan di kelas dan anggota yang diwarisi dari kelas dasar (kecuali untuk kelas object
yang tidak memiliki kelas dasar). Anggota yang diwarisi dari kelas dasar mencakup konstanta, bidang, metode, properti, peristiwa, pengindeks, operator, dan jenis kelas dasar, tetapi bukan konstruktor instans, finalizer, dan konstruktor statis kelas dasar. Anggota kelas dasar diwariskan tanpa memperhatikan aksesibilitas mereka.
Deklarasi kelas dapat berisi deklarasi konstanta, bidang, metode, properti, peristiwa, pengindeks, operator, konstruktor instans, finalizer, konstruktor statis, dan jenis.
Anggota object
(§8.2.3) dan string
(§8.2.5) sesuai langsung dengan anggota jenis kelas yang mereka alias.
7.4.6 Anggota antarmuka
Anggota antarmuka adalah anggota yang dideklarasikan dalam antarmuka dan di semua antarmuka dasar antarmuka.
Catatan: Anggota di kelas
object
tidak, secara ketat berbicara, anggota antarmuka apa pun (§18.4). Namun, anggota di kelasobject
tersedia melalui pencarian anggota dalam jenis antarmuka apa pun (§12,5). catatan akhir
7.4.7 Anggota array
Anggota array adalah anggota yang diwarisi dari kelas System.Array
.
7.4.8 Mendelegasikan anggota
Delegasi mewarisi anggota dari kelas System.Delegate
. Selain itu, ini berisi metode Invoke
bernama dengan jenis pengembalian dan daftar parameter yang sama yang ditentukan dalam deklarasinya (§20.2). Pemanggilan metode ini harus bereaksi identik dengan pemanggilan delegasi (§20,6) pada instans delegasi yang sama.
Implementasi dapat memberikan anggota tambahan, baik melalui warisan atau langsung di delegasi itu sendiri.
7.5 Akses anggota
7.5.1 Umum
Deklarasi anggota memungkinkan kontrol atas akses anggota. Aksesibilitas anggota ditetapkan oleh aksesibilitas yang dinyatakan (§7.5.2) anggota yang dikombinasikan dengan aksesibilitas jenis yang segera berisi, jika ada.
Ketika akses ke anggota tertentu diizinkan, anggota dikatakan dapat diakses. Sebaliknya, ketika akses ke anggota tertentu tidak diizinkan, anggota tersebut dikatakan tidak dapat diakses. Akses ke anggota diizinkan ketika lokasi tekstual tempat akses terjadi disertakan dalam domain aksesibilitas (§7.5.3) anggota.
7.5.2 Aksesibilitas yang dinyatakan
Aksesibilitas anggota yang dinyatakan dapat berupa salah satu hal berikut:
- Publik, yang dipilih dengan menyertakan pengubah
public
dalam deklarasi anggota. Arti intuitif daripublic
adalah "akses tidak terbatas". - Dilindungi, yang dipilih dengan menyertakan pengubah
protected
dalam deklarasi anggota. Arti intuitif adalahprotected
"akses terbatas pada kelas atau jenis yang berisi yang berasal dari kelas yang berisi". - Internal, yang dipilih dengan menyertakan pengubah
internal
dalam deklarasi anggota. Arti intuitif dariinternal
adalah "akses terbatas pada perakitan ini". - Internal terlindungi, yang dipilih dengan menyertakan dan
protected
pengubahinternal
dalam deklarasi anggota. Arti intuitif adalahprotected internal
"dapat diakses dalam rakitan ini serta jenis yang berasal dari kelas yang berisi". - Dilindungi privat, yang dipilih dengan menyertakan dan
private
protected
pengubah dalam deklarasi anggota. Arti intuitif adalahprivate protected
"dapat diakses dalam rakitan ini oleh kelas dan jenis yang berisi yang berasal dari kelas yang berisi." - Privat, yang dipilih dengan menyertakan pengubah
private
dalam deklarasi anggota. Arti intuitif dariprivate
adalah "akses terbatas pada jenis yang berisi".
Tergantung pada konteks di mana deklarasi anggota berlangsung, hanya jenis aksesibilitas tertentu yang dinyatakan yang diizinkan. Selain itu, ketika deklarasi anggota tidak menyertakan pengubah akses apa pun, konteks di mana deklarasi berlangsung menentukan aksesibilitas yang dinyatakan default.
- Namespace secara implisit telah
public
mendeklarasikan aksesibilitas. Tidak ada pengubah akses yang diizinkan pada deklarasi namespace layanan. - Jenis yang dideklarasikan langsung dalam unit kompilasi atau namespace (dibandingkan dengan dalam jenis lain) dapat memiliki
public
atauinternal
mendeklarasikan aksesibilitas dan default keinternal
aksesibilitas yang dinyatakan. - Anggota kelas dapat memiliki salah satu jenis aksesibilitas yang dinyatakan dan default yang diizinkan untuk
private
aksesibilitas yang dinyatakan.Catatan: Jenis yang dinyatakan sebagai anggota kelas dapat memiliki salah satu jenis aksesibilitas yang dinyatakan yang diizinkan, sedangkan jenis yang dinyatakan sebagai anggota namespace hanya
public
dapat memiliki atauinternal
mendeklarasikan aksesibilitas. catatan akhir - Anggota struct dapat memiliki
public
aksesibilitas ,internal
, atauprivate
dinyatakan dan default keprivate
aksesibilitas yang dinyatakan karena struktur secara implisit disegel. Anggota struct yang diperkenalkan dalamstruct
(yaitu, tidak diwariskan oleh struktur tersebut) tidak dapat memilikiprotected
aksesibilitas , ,protected internal
atauprivate protected
dinyatakan.Catatan: Jenis yang dinyatakan sebagai anggota struktur dapat memiliki
public
aksesibilitas , ,internal
atauprivate
dinyatakan, sedangkan jenis yang dinyatakan sebagai anggota namespace hanyapublic
dapat memiliki atauinternal
menyatakan aksesibilitas. catatan akhir - Anggota antarmuka secara implisit telah
public
mendeklarasikan aksesibilitas. Tidak ada pengubah akses yang diizinkan pada deklarasi anggota antarmuka. - Anggota enumerasi secara implisit telah
public
menyatakan aksesibilitas. Tidak ada pengubah akses yang diizinkan pada deklarasi anggota enumerasi.
7.5.3 Domain aksesibilitas
Domain aksesibilitas anggota terdiri dari bagian (mungkin terputus-putus) teks program di mana akses ke anggota diizinkan. Untuk tujuan mendefinisikan domain aksesibilitas anggota, anggota dikatakan tingkat atas jika tidak dideklarasikan dalam jenis, dan anggota dikatakan bersarang jika dinyatakan dalam jenis lain. Selain itu, teks program program didefinisikan sebagai semua teks yang terkandung dalam semua unit kompilasi program, dan teks program jenis didefinisikan sebagai semua teks yang terkandung dalam type_declarationjenis tersebut (termasuk, mungkin, jenis yang disarangkan dalam jenis).
Domain aksesibilitas dari jenis yang telah ditentukan sebelumnya (seperti object
, , int
atau double
) tidak terbatas.
Domain aksesibilitas dari jenis T
tidak terikat tingkat atas (§8.4.4) yang dideklarasikan dalam program P
didefinisikan sebagai berikut:
- Jika aksesibilitas
T
yang dinyatakan adalah publik, domainT
aksesibilitas adalah teksP
program dan program apa pun yang mereferensikanP
. - Jika aksesibilitas
T
yang dinyatakan bersifat internal, domainT
aksesibilitas adalah teks program .P
Catatan: Dari definisi ini, mengikuti bahwa domain aksesibilitas dari jenis unbound tingkat atas selalu setidaknya teks program program di mana jenis tersebut dideklarasikan. catatan akhir
Domain aksesibilitas untuk jenis T<A₁, ..., Aₑ>
yang dibangun adalah persimpangan domain aksesibilitas dari jenis T
generik yang tidak terikat dan domain aksesibilitas dari argumen A₁, ..., Aₑ
jenis .
Domain aksesibilitas anggota M
berlapis yang dideklarasikan dalam jenis T
dalam program P
, didefinisikan sebagai berikut (mencatat bahwa M
itu sendiri mungkin jenis):
- Jika aksesibilitas yang dideklarasikan
M
adalahpublic
, domain aksesibilitasM
adalah domain aksesibilitasT
. - Jika aksesibilitas
M
yang dinyatakan adalahprotected internal
, mari kitaD
menjadi persatuan teksP
program dan teks program dari jenis apa pun yang berasal dariT
, yang dideklarasikan di luarP
. Domain aksesibilitasM
adalah persimpangan domainT
aksesibilitas denganD
. - Jika aksesibilitas
M
yang dinyatakan adalahprivate protected
, biarkanD
menjadi persimpangan teksP
program dan teks program dariT
dan jenis apa pun yang berasal dariT
. Domain aksesibilitasM
adalah persimpangan domainT
aksesibilitas denganD
. - Jika aksesibilitas
M
yang dinyatakan adalahprotected
, mari kitaD
menjadi gabungan teksT
program dan teks program dari jenis apa pun yang berasal dariT
. Domain aksesibilitasM
adalah persimpangan domainT
aksesibilitas denganD
. - Jika aksesibilitas yang dideklarasikan
M
adalahinternal
, domain aksesibilitasM
adalah persimpangan domain aksesibilitasT
dengan teks programP
. - Jika aksesibilitas yang dideklarasikan
M
adalahprivate
, domain aksesibilitasM
adalah teks programT
.
Catatan: Dari definisi ini, berikut adalah bahwa domain aksesibilitas anggota berlapis selalu setidaknya merupakan teks program dari jenis di mana anggota dideklarasikan. Selain itu, ini mengikuti bahwa domain aksesibilitas anggota tidak pernah lebih inklusif daripada domain aksesibilitas jenis tempat anggota dideklarasikan. catatan akhir
Catatan: Dalam istilah intuitif, saat jenis atau anggota
M
diakses, langkah-langkah berikut dievaluasi untuk memastikan bahwa akses diizinkan:
- Pertama, jika
M
dideklarasikan dalam jenis (dibandingkan dengan unit kompilasi atau namespace), kesalahan waktu kompilasi terjadi jika jenis tersebut tidak dapat diakses.- Kemudian, jika
M
adalahpublic
, akses diizinkan.- Jika tidak, jika
M
adalahprotected internal
, akses diizinkan jika terjadi dalam program yangM
dinyatakan, atau jika terjadi dalam kelas yang berasal dari kelas yangM
dideklarasikan dan berlangsung melalui jenis kelas turunan (§7.5.4).- Jika tidak, jika
M
adalahprotected
, akses diizinkan jika terjadi dalam kelas yangM
dinyatakan, atau jika terjadi dalam kelas yang berasal dari kelas yangM
dideklarasikan dan berlangsung melalui jenis kelas turunan (§7.5.4).- Jika tidak, jika
M
adalahinternal
, akses diizinkan jika terjadi dalam program di manaM
dinyatakan.- Jika tidak, jika
M
adalahprivate
, akses diizinkan jika terjadi dalam jenis yang dinyatakanM
.- Jika tidak, jenis atau anggota tidak dapat diakses, dan terjadi kesalahan waktu kompilasi. catatan akhir
Contoh: Dalam kode berikut
public class A { public static int X; internal static int Y; private static int Z; } internal class B { public static int X; internal static int Y; private static int Z; public class C { public static int X; internal static int Y; private static int Z; } private class D { public static int X; internal static int Y; private static int Z; } }
kelas dan anggota memiliki domain aksesibilitas berikut:
- Domain aksesibilitas
A
danA.X
tidak terbatas.- Domain aksesibilitas dari
A.Y
, ,B
,B.X
B.Y
,B.C
,B.C.X
, , danB.C.Y
adalah teks program dari program yang berisi.- Domain aksesibilitas adalah
A.Z
teks program dariA
.- Domain aksesibilitas
B.Z
danB.D
adalah teks program dariB
, termasuk teks program danB.C
B.D
.- Domain aksesibilitas adalah
B.C.Z
teks program dariB.C
.- Domain aksesibilitas
B.D.X
danB.D.Y
adalah teks program dariB
, termasuk teks program danB.C
B.D
.- Domain aksesibilitas adalah
B.D.Z
teks program dariB.D
. Seperti yang diilustrasikan contoh, domain aksesibilitas anggota tidak pernah lebih besar dari jenis yang berisi. Misalnya, meskipun semuaX
anggota memiliki aksesibilitas yang dinyatakan publik, semua tetapiA.X
memiliki domain aksesibilitas yang dibatasi oleh jenis yang berisi.contoh akhir
Seperti yang dijelaskan dalam §7.4, semua anggota kelas dasar, kecuali untuk konstruktor instans, finalizer, dan konstruktor statis, diwariskan oleh jenis turunan. Ini termasuk bahkan anggota privat dari kelas dasar. Namun, domain aksesibilitas anggota privat hanya mencakup teks program dari jenis di mana anggota dideklarasikan.
Contoh: Dalam kode berikut
class A { int x; static void F(B b) { b.x = 1; // Ok } } class B : A { static void F(B b) { b.x = 1; // Error, x not accessible } }
B
kelas mewarisi anggotax
privat dariA
kelas . Karena anggota bersifat pribadi, anggota hanya dapat diakses dalam class_body .A
Dengan demikian, akses untukb.x
berhasil dalamA.F
metode , tetapi gagal dalamB.F
metode .contoh akhir
7.5.4 Akses terlindungi
protected
Ketika anggota instans atau private protected
diakses di luar teks program kelas di mana ia dideklarasikan, dan ketika protected internal
anggota instans diakses di luar teks program program tempat program dideklarasikan, akses akan berlangsung dalam deklarasi kelas yang berasal dari kelas di mana ia dideklarasikan. Selain itu, akses diperlukan untuk dilakukan melalui instans jenis kelas turunan atau jenis kelas yang dibangun darinya. Pembatasan ini mencegah satu kelas turunan mengakses anggota yang dilindungi dari kelas turunan lainnya, bahkan ketika anggota diwariskan dari kelas dasar yang sama.
Mari kita B
menjadi kelas dasar yang mendeklarasikan anggota M
instans yang dilindungi , dan biarkan D
menjadi kelas yang berasal dari B
.
Dalam class_bodyD
, akses ke M
dapat mengambil salah satu formulir berikut:
- Type_name.
- Primary_expression formulir , asalkan jenis adalah
E.M
atau kelas yang berasal dariE
, di manaT
adalah kelasT
, atau jenis kelas yang dibangun dariT
.D
D
-
Primary_expression formulir
base.M
. -
Primary_expression formulir
base[
argument_list]
.
Selain bentuk akses ini, kelas turunan dapat mengakses konstruktor instans yang dilindungi dari kelas dasar dalam constructor_initializer (§15.11.2).
Contoh: Dalam kode berikut
public class A { protected int x; static void F(A a, B b) { a.x = 1; // Ok b.x = 1; // Ok } } public class B : A { static void F(A a, B b) { a.x = 1; // Error, must access through instance of B b.x = 1; // Ok } }
dalam
A
, dimungkinkan untuk mengaksesx
melalui instans danA
B
, karena dalam kedua kasus akses berlangsung melalui instansA
atau kelas yang berasal dariA
. Namun, dalamB
, tidak mungkin untuk mengaksesx
melalui instansA
, karenaA
tidak berasal dariB
.contoh akhir
Contoh:
class C<T> { protected T x; } class D<T> : C<T> { static void F() { D<T> dt = new D<T>(); D<int> di = new D<int>(); D<string> ds = new D<string>(); dt.x = default(T); di.x = 123; ds.x = "test"; } }
Di sini, tiga tugas
x
diizinkan karena semuanya berlangsung melalui instans jenis kelas yang dibangun dari jenis generik.contoh akhir
Catatan: Domain aksesibilitas (§7.5.3) anggota yang dilindungi yang dideklarasikan dalam kelas generik mencakup teks program dari semua deklarasi kelas yang berasal dari jenis apa pun yang dibangun dari kelas generik tersebut. Dalam contoh:
class C<T> { protected static T x; } class D : C<string> { static void Main() { C<int>.x = 5; } }
referensi ke
protected
anggotaC<int>.x
diD
valid meskipun kelasD
berasal dariC<string>
. catatan akhir
7.5.5 Batasan aksesibilitas
Beberapa konstruksi dalam bahasa C# mengharuskan jenis setidaknya dapat diakses sebagai anggota atau jenis lain. Jenis T
dikatakan setidaknya dapat diakses sebagai anggota atau jenis M
jika domain T
aksesibilitas adalah superset dari domain M
aksesibilitas . Dengan kata lain, T
setidaknya dapat diakses seolah-olah M
T
dapat diakses dalam semua konteks di mana M
dapat diakses.
Ada batasan aksesibilitas berikut:
- Kelas dasar langsung dari jenis kelas harus setidaknya dapat diakses sebagai jenis kelas itu sendiri.
- Antarmuka dasar eksplisit dari jenis antarmuka harus setidaknya dapat diakses seperti jenis antarmuka itu sendiri.
- Jenis pengembalian dan jenis parameter dari jenis delegasi harus setidaknya dapat diakses sebagai jenis delegasi itu sendiri.
- Jenis konstanta harus setidaknya dapat diakses sebagai konstanta itu sendiri.
- Jenis bidang harus setidaknya dapat diakses sebagai bidang itu sendiri.
- Jenis pengembalian dan jenis parameter metode harus setidaknya dapat diakses sebagai metode itu sendiri.
- Jenis properti harus setidaknya dapat diakses sebagai properti itu sendiri.
- Jenis peristiwa harus setidaknya dapat diakses sebagai peristiwa itu sendiri.
- Jenis dan jenis parameter pengindeks harus setidaknya dapat diakses sebagai pengindeks itu sendiri.
- Jenis pengembalian dan jenis parameter operator harus setidaknya dapat diakses seperti operator itu sendiri.
- Jenis parameter konstruktor instans harus setidaknya dapat diakses sebagai konstruktor instans itu sendiri.
- Batasan antarmuka atau jenis kelas pada parameter jenis harus setidaknya dapat diakses sebagai anggota yang menyatakan batasan.
Contoh: Dalam kode berikut
class A {...} public class B: A {...}
B
kelas menghasilkan kesalahan waktu kompilasi karenaA
setidaknya tidak dapat diakses sebagaiB
.contoh akhir
Contoh: Demikian juga, dalam kode berikut
class A {...} public class B { A F() {...} internal A G() {...} public A H() {...} }
H
metode dalamB
menghasilkan kesalahan waktu kompilasi karena jenisA
pengembalian setidaknya tidak dapat diakses sebagai metode .contoh akhir
7.6 Tanda Tangan dan kelebihan beban
Metode, konstruktor instans, pengindeks, dan operator dicirikan oleh tanda tangannya:
- Tanda tangan metode terdiri dari nama metode, jumlah parameter jenis, dan mode jenis dan melewati parameter dari setiap parameternya, yang dipertimbangkan dalam urutan kiri ke kanan. Untuk tujuan ini, parameter jenis apa pun dari metode yang terjadi dalam jenis parameter diidentifikasi bukan dengan namanya, tetapi dengan posisi ordinalnya dalam daftar parameter jenis metode. Tanda tangan metode secara khusus tidak mencakup jenis pengembalian, nama parameter, nama parameter jenis, batasan parameter jenis,
params
pengubah parameter atauthis
, atau apakah parameter diperlukan atau opsional. - Tanda tangan konstruktor instans terdiri dari jenis dan mode melewati parameter dari setiap parameternya, yang dipertimbangkan dalam urutan kiri ke kanan. Tanda tangan konstruktor instans secara khusus tidak menyertakan
params
pengubah yang dapat ditentukan untuk parameter paling kanan, atau apakah parameter diperlukan atau opsional. - Tanda tangan pengindeks terdiri dari jenis setiap parameternya, yang dipertimbangkan dalam urutan kiri ke kanan. Tanda tangan pengindeks secara khusus tidak menyertakan jenis elemen, juga tidak menyertakan
params
pengubah yang mungkin ditentukan untuk parameter paling kanan, atau apakah parameter diperlukan atau opsional. - Tanda tangan operator terdiri dari nama operator dan jenis setiap parameternya, yang dipertimbangkan dalam urutan kiri ke kanan. Tanda tangan operator secara khusus tidak menyertakan jenis hasil.
- Tanda tangan operator konversi terdiri dari jenis sumber dan jenis target. Klasifikasi implisit atau eksplisit operator konversi bukan bagian dari tanda tangan.
- Dua tanda tangan dari jenis anggota yang sama (metode, konstruktor instans, pengindeks, atau operator) dianggap sebagai tanda tangan yang sama jika mereka memiliki nama yang sama, jumlah parameter jenis, jumlah parameter, dan mode yang melewati parameter, dan konversi identitas ada di antara jenis parameter yang sesuai (§10.2.2).
Tanda tangan adalah mekanisme pengaktifan untuk kelebihan beban anggota di kelas, struktur, dan antarmuka:
- Kelebihan beban metode memungkinkan kelas, struct, atau antarmuka untuk mendeklarasikan beberapa metode dengan nama yang sama, asalkan tanda tangan mereka unik dalam kelas, struct, atau antarmuka tersebut.
- Kelebihan beban konstruktor instans memungkinkan kelas atau struktur untuk mendeklarasikan beberapa konstruktor instans, asalkan tanda tangan mereka unik dalam kelas atau struktur tersebut.
- Kelebihan beban pengindeks memungkinkan kelas, struct, atau antarmuka untuk mendeklarasikan beberapa pengindeks, asalkan tanda tangan mereka unik dalam kelas, struct, atau antarmuka tersebut.
- Kelebihan beban operator memungkinkan kelas atau struktur untuk mendeklarasikan beberapa operator dengan nama yang sama, asalkan tanda tangan mereka unik dalam kelas atau struktur tersebut.
Meskipun in
pengubah parameter , out
, dan ref
dianggap sebagai bagian dari tanda tangan, anggota yang dinyatakan dalam satu jenis tidak dapat berbeda dalam tanda tangan hanya dengan in
, out
, dan ref
. Kesalahan waktu kompilasi terjadi jika dua anggota dinyatakan dalam jenis yang sama dengan tanda tangan yang akan sama jika semua parameter dalam kedua metode dengan out
atau in
pengubah diubah menjadi ref
pengubah. Untuk tujuan lain pencocokan tanda tangan (misalnya, menyembunyikan atau mengambil alih), in
, , out
, dan ref
dianggap sebagai bagian dari tanda tangan dan tidak cocok satu sama lain.
Catatan: Pembatasan ini untuk memungkinkan program C# dengan mudah diterjemahkan untuk dijalankan pada Common Language Infrastructure (CLI), yang tidak menyediakan cara untuk menentukan metode yang hanya berbeda dalam
in
, ,out
danref
. catatan akhir
Jenis object
dan dynamic
tidak dibedakan saat membandingkan tanda tangan. Oleh karena itu anggota dinyatakan dalam satu jenis yang tanda tangannya berbeda hanya dengan mengganti object
dengan dynamic
tidak diizinkan.
Contoh: Contoh berikut menunjukkan serangkaian deklarasi metode yang kelebihan beban bersama dengan tanda tangannya.
interface ITest { void F(); // F() void F(int x); // F(int) void F(ref int x); // F(ref int) void F(out int x); // F(out int) error void F(object o); // F(object) void F(dynamic d); // error. void F(int x, int y); // F(int, int) int F(string s); // F(string) int F(int x); // F(int) error void F(string[] a); // F(string[]) void F(params string[] a); // F(string[]) error void F<S>(S s); // F<0>(0) void F<T>(T t); // F<0>(0) error void F<S,T>(S s); // F<0,1>(0) void F<T,S>(S s); // F<0,1>(1) ok }
Perhatikan bahwa pengubah parameter , , dan
in
apa punout
(ref
) adalah bagian dari tanda tangan. Dengan demikian,F(int)
,F(in int)
,F(out int)
, danF(ref int)
semua tanda tangan unik. Namun,F(in int)
,F(out int)
, danF(ref int)
tidak dapat dideklarasikan dalam antarmuka yang sama karena tanda tangan mereka hanya berbeda denganin
, ,out
danref
. Selain itu, perhatikan bahwa jenis pengembalian danparams
pengubah bukan bagian dari tanda tangan, sehingga tidak dimungkinkan untuk membebani hanya berdasarkan jenis pengembalian atau pada penyertaan atau pengecualian pengubahparams
. Dengan demikian, deklarasi metodeF(int)
danF(params string[])
diidentifikasi di atas, mengakibatkan kesalahan waktu kompilasi. contoh akhir
7.7 Cakupan
7.7.1 Umum
Cakupan nama adalah wilayah teks program di mana dimungkinkan untuk merujuk ke entitas yang dideklarasikan dengan nama tanpa kualifikasi nama. Cakupan dapat ditumpuk, dan cakupan dalam dapat mendeklarasi ulang arti nama dari cakupan luar. (Namun, ini tidak menghapus pembatasan yang diberlakukan oleh §7.3 bahwa dalam blok berlapis tidak dimungkinkan untuk mendeklarasikan variabel lokal atau konstanta lokal dengan nama yang sama dengan variabel lokal atau konstanta lokal dalam blok penutup.) Nama dari cakupan luar kemudian dikatakan tersembunyi di wilayah teks program yang dicakup oleh cakupan dalam, dan akses ke nama luar hanya dimungkinkan dengan memenuhi syarat nama.
Cakupan anggota namespace yang dideklarasikan oleh namespace_member_declaration (§14,6) tanpa menyertakan namespace_declaration adalah seluruh teks program.
Cakupan anggota namespace yang dideklarasikan oleh namespace_member_declaration dalam namespace_declaration dengan nama yang sepenuhnya memenuhi syarat adalah , adalah namespace_body dari setiap
N
yang namanya sepenuhnya memenuhi syarat adalah atau dimulai dengan , diikuti oleh titik.N
N
Cakupan nama yang ditentukan oleh extern_alias_directive (§14,4) meluas di atas using_directive, global_attributes, dan namespace_member_declarationyang segera berisi compilation_unit atau namespace_body. Extern_alias_directive tidak menyumbangkan anggota baru ke ruang deklarasi yang mendasar. Dengan kata lain, extern_alias_directive tidak transitif, melainkan hanya memengaruhi compilation_unit atau namespace_body di mana itu terjadi.
Cakupan nama yang ditentukan atau diimpor oleh using_directive (§14,5) meluas di atas global_attributes dan namespace_member_declarationcompilation_unitatau namespace_body tempat using_directive terjadi. Using_directive dapat membuat nol atau lebih namespace layanan atau nama jenis tersedia dalam compilation_unit atau namespace_body tertentu, tetapi tidak menyumbangkan anggota baru ke ruang deklarasi yang mendasar. Dengan kata lain, using_directive tidak transitif melainkan hanya memengaruhi compilation_unit atau namespace_body di mana itu terjadi.
Cakupan parameter jenis yang dideklarasikan oleh type_parameter_list pada class_declaration (§15,2) adalah class_base, type_parameter_constraints_clause, dan class_bodyclass_declaration tersebut.
Catatan: Tidak seperti anggota kelas, cakupan ini tidak meluas ke kelas turunan. catatan akhir
Cakupan parameter jenis yang dideklarasikan oleh type_parameter_list pada struct_declaration (§16,2) adalah struct_interfaces, type_parameter_constraints_clause, dan struct_body dari struct_declaration tersebut.
Cakupan parameter jenis yang dideklarasikan oleh type_parameter_list pada interface_declaration (§18.2) adalah interface_base, type_parameter_constraints_clause, dan interface_body dari interface_declaration tersebut.
Cakupan parameter jenis yang dideklarasikan oleh type_parameter_list pada delegate_declaration (§20,2) adalah return_type, parameter_list, dan type_parameter_constraints_clausedari delegate_declaration tersebut.
Cakupan parameter jenis yang dideklarasikan oleh type_parameter_list pada method_declaration (§15.6.1) adalah method_declaration.
Cakupan anggota yang dideklarasikan oleh class_member_declaration (§15.3.1) adalah class_body tempat deklarasi terjadi. Selain itu, cakupan anggota kelas meluas ke class_body kelas turunan yang disertakan dalam domain aksesibilitas (§7.5.3) anggota.
Cakupan anggota yang dideklarasikan oleh struct_member_declaration (§16,3) adalah struct_body tempat deklarasi terjadi.
Cakupan anggota yang dideklarasikan oleh enum_member_declaration (§19,4) adalah enum_body di mana deklarasi terjadi.
Cakupan parameter yang dideklarasikan dalam method_declaration (§15,6) adalah method_body atau ref_method_body method_declaration tersebut.
Cakupan parameter yang dideklarasikan dalam indexer_declaration (§15,9) adalah indexer_body dari indexer_declaration tersebut.
Cakupan parameter yang dideklarasikan dalam operator_declaration (§15,10) adalah operator_body dari operator_declaration tersebut.
Cakupan parameter yang dideklarasikan dalam constructor_declaration (§15,11) adalah constructor_initializer dan blok constructor_declaration tersebut.
Cakupan parameter yang dideklarasikan dalam lambda_expression (§12,19) adalah lambda_expression_body dari lambda_expression tersebut.
Cakupan parameter yang dideklarasikan dalam anonymous_method_expression (§12,19) adalah blok dari anonymous_method_expression tersebut.
Cakupan label yang dideklarasikan dalam labeled_statement (§13,5) adalah blok tempat deklarasi terjadi.
Cakupan variabel lokal yang dideklarasikan dalam local_variable_declaration (§13.6.2) adalah blok tempat deklarasi terjadi.
Cakupan variabel lokal yang dideklarasikan dalam switch_block
switch
pernyataan (§13.8.3) adalah switch_block.Cakupan variabel lokal yang dideklarasikan dalam for_initializer
for
pernyataan (§13.9.4) adalah for_initializer, for_condition, for_iterator, dan embedded_statementfor
pernyataan.Cakupan konstanta lokal yang dideklarasikan dalam local_constant_declaration (§13.6.3) adalah blok tempat deklarasi terjadi. Ini adalah kesalahan waktu kompilasi untuk merujuk ke konstanta lokal dalam posisi tekstual yang mendahului constant_declarator.
Cakupan variabel yang dideklarasikan sebagai bagian dari foreach_statement, using_statement, lock_statement , atau query_expression ditentukan oleh perluasan konstruksi yang diberikan.
Dalam cakupan namespace, kelas, struct, atau anggota enumerasi dimungkinkan untuk merujuk ke anggota dalam posisi tekstual yang mendahului deklarasi anggota.
Contoh:
class A { void F() { i = 1; } int i = 0; }
Di sini, valid untuk
F
merujuki
sebelum dinyatakan.contoh akhir
Dalam cakupan variabel lokal, ini adalah kesalahan waktu kompilasi untuk merujuk ke variabel lokal dalam posisi tekstual yang mendahului deklaratornya.
Contoh:
class A { int i = 0; void F() { i = 1; // Error, use precedes declaration int i; i = 2; } void G() { int j = (j = 1); // Valid } void H() { int a = 1, b = ++a; // Valid } }
Dalam metode di
F
atas, penugasan pertama untuki
secara khusus tidak merujuk ke bidang yang dideklarasikan dalam cakupan luar. Sebaliknya, ini mengacu pada variabel lokal dan menghasilkan kesalahan waktu kompilasi karena secara tekstual mendahului deklarasi variabel.G
Dalam metode , penggunaanj
dalam penginisialisasi untuk deklarasij
valid karena penggunaan tidak mendahului deklarator. Dalam metode ,H
deklarator berikutnya dengan benar mengacu pada variabel lokal yang dideklarasikan dalam deklarator sebelumnya dalam local_variable_declaration yang sama.contoh akhir
Catatan: Aturan cakupan untuk variabel lokal dan konstanta lokal dirancang untuk menjamin bahwa arti nama yang digunakan dalam konteks ekspresi selalu sama dalam blok. Jika cakupan variabel lokal hanya akan diperpanjang dari deklarasinya ke akhir blok, maka dalam contoh di atas, penugasan pertama akan ditetapkan ke variabel instans dan penetapan kedua akan ditetapkan ke variabel lokal, mungkin mengarah ke kesalahan waktu kompilasi jika pernyataan blok nantinya akan diatur ulang.)
Arti nama dalam blok mungkin berbeda berdasarkan konteks di mana nama digunakan. Dalam contoh
class A {} class Test { static void Main() { string A = "hello, world"; string s = A; // expression context Type t = typeof(A); // type context Console.WriteLine(s); // writes "hello, world" Console.WriteLine(t); // writes "A" } }
nama
A
digunakan dalam konteks ekspresi untuk merujuk ke variabelA
lokal dan dalam konteks jenis untuk merujuk ke kelasA
.catatan akhir
7.7.2 Nama bersembunyi
7.7.2.1 Umum
Cakupan entitas biasanya mencakup lebih banyak teks program daripada ruang deklarasi entitas. Secara khusus, cakupan entitas dapat mencakup deklarasi yang memperkenalkan ruang deklarasi baru yang berisi entitas dengan nama yang sama. Deklarasi tersebut menyebabkan entitas asli menjadi tersembunyi. Sebaliknya, entitas dikatakan terlihat ketika tidak disembunyikan.
Persembunyian nama terjadi ketika cakupan tumpang tindih melalui berlapis dan ketika cakupan tumpang tindih melalui pewarisan. Karakteristik dari dua jenis persembunyian dijelaskan dalam subklaus berikut.
7.7.2.2 Bersembunyi melalui bersarang
Nama yang bersembunyi melalui bersarang dapat terjadi sebagai akibat dari menyarangkan namespace layanan atau jenis dalam namespace, sebagai akibat dari jenis berlapis dalam kelas atau struktur, sebagai akibat dari fungsi lokal atau lambda, dan sebagai hasil dari parameter, variabel lokal, dan deklarasi konstanta lokal.
Contoh: Dalam kode berikut
class A { int i = 0; void F() { int i = 1; void M1() { float i = 1.0f; Func<double, double> doubler = (double i) => i * 2.0; } } void G() { i = 1; } }
F
dalam metode , variabeli
instans disembunyikan oleh variabeli
lokal , tetapi dalamG
metode ,i
masih mengacu pada variabel instans. Di dalam fungsiM1
float i
lokal menyembunyikan langsung-luari
. Parameteri
lambda menyembunyikan bagianfloat i
dalam tubuh lambda.contoh akhir
Ketika nama dalam lingkup dalam menyembunyikan nama dalam cakupan luar, nama menyembunyikan semua kejadian yang kelebihan beban nama tersebut.
Contoh: Dalam kode berikut
class Outer { static void F(int i) {} static void F(string s) {} class Inner { static void F(long l) {} void G() { F(1); // Invokes Outer.Inner.F F("Hello"); // Error } } }
panggilan
F(1)
memanggilF
yang dideklarasikan karenaInner
semua kejadianF
luar disembunyikan oleh deklarasi bagian dalam. Untuk alasan yang sama, panggilanF("Hello")
menghasilkan kesalahan waktu kompilasi.contoh akhir
7.7.2.3 Bersembunyi melalui warisan
Nama yang bersembunyi melalui pewarisan terjadi ketika kelas atau struktur mendeklarasi ulang nama yang diwarisi dari kelas dasar. Jenis persembunyian nama ini mengambil salah satu formulir berikut:
- Konstanta, bidang, properti, peristiwa, atau jenis yang diperkenalkan di kelas atau struktur menyembunyikan semua anggota kelas dasar dengan nama yang sama.
- Metode yang diperkenalkan di kelas atau struktur menyembunyikan semua anggota kelas dasar non-metode dengan nama yang sama, dan semua metode kelas dasar dengan tanda tangan yang sama (§7,6).
- Pengindeks yang diperkenalkan di kelas atau struktur menyembunyikan semua pengindeks kelas dasar dengan tanda tangan yang sama (§7.6) .
Aturan yang mengatur deklarasi operator (§15.10) membuat kelas turunan tidak mungkin mendeklarasikan operator dengan tanda tangan yang sama dengan operator di kelas dasar. Dengan demikian, operator tidak pernah menyembunyikan satu dengan yang lain.
Berlawanan dengan menyembunyikan nama dari cakupan luar, menyembunyikan nama yang terlihat dari cakupan yang diwariskan menyebabkan peringatan dilaporkan.
Contoh: Dalam kode berikut
class Base { public void F() {} } class Derived : Base { public void F() {} // Warning, hiding an inherited name }
deklarasi
F
dalamDerived
menyebabkan peringatan dilaporkan. Menyembunyikan nama yang diwariskan secara khusus bukan kesalahan, karena itu akan menghalangi evolusi kelas dasar yang terpisah. Misalnya, situasi di atas mungkin muncul karena versiBase
yang lebih baru memperkenalkanF
metode yang tidak ada di versi kelas yang lebih lama.contoh akhir
Peringatan yang disebabkan oleh menyembunyikan nama yang diwariskan dapat dihilangkan melalui penggunaan pengubah new
:
Contoh:
class Base { public void F() {} } class Derived : Base { public new void F() {} }
Pengubah
new
menunjukkan bahwaF
inDerived
adalah "baru", dan bahwa itu memang dimaksudkan untuk menyembunyikan anggota yang diwariskan.contoh akhir
Deklarasi anggota baru menyembunyikan anggota yang diwariskan hanya dalam lingkup anggota baru.
Contoh:
class Base { public static void F() {} } class Derived : Base { private new static void F() {} // Hides Base.F in Derived only } class MoreDerived : Derived { static void G() { F(); // Invokes Base.F } }
Dalam contoh di atas, deklarasi dalam menyembunyikan yang diwariskan dari
F
, tetapi karena yang baruDerived
memilikiF
akses privat, cakupannya tidak meluas keBase
.F
Derived
MoreDerived
Dengan demikian, panggilanF()
masukMoreDerived.G
valid dan akan memanggilBase.F
.contoh akhir
7.8 Namespace layanan dan nama jenis
7.8.1 Umum
Beberapa konteks dalam program C# memerlukan namespace_name atau type_name untuk ditentukan.
namespace_name
: namespace_or_type_name
;
type_name
: namespace_or_type_name
;
namespace_or_type_name
: identifier type_argument_list? ('.' identifier type_argument_list?)*
| qualified_alias_member ('.' identifier type_argument_list?)*
;
namespace_name adalah namespace_or_type_name yang mengacu pada namespace.
Resolusi berikut seperti yang dijelaskan di bawah ini, namespace_or_type_namenamespace_name harus merujuk ke namespace layanan, atau jika tidak, terjadi kesalahan waktu kompilasi. Tidak ada argumen jenis (§8.4.2) yang dapat ada dalam namespace_name (hanya jenis yang dapat memiliki argumen jenis).
type_name adalah namespace_or_type_name yang mengacu pada jenis.
Resolusi berikut seperti yang dijelaskan di bawah ini, namespace_or_type_nametype_name akan merujuk pada jenis, atau jika tidak, terjadi kesalahan waktu kompilasi.
namespace_or_type_name mengacu pada jenis atau namespace. Resolusi untuk namespace atau tipe tertentu melibatkan dua tahap berdasarkan pembagian tata bahasa menjadi bagian awal, yang terdiri dari salah satu elemen tata bahasa:
identifier type_argument_list?
qualified_alias_member
dan bagian berikutnya, yaitu fragmen tata bahasa
('.' identifier type_argument_list?)*
Pertama, bagian awal ditentukan untuk menentukan R₀
, namespace atau tipe awal.
Jika bagian terdepan dari namespace_or_type_name adalah qualified_alias_member, maka R₀
adalah namespace atau jenis yang diidentifikasi dengan menyelesaikannya seperti yang dijelaskan dalam §14.8.1.
Jika tidak, bagian terdepan, sebagai fragmen tata bahasa pengidentifikasi type_argument_list?, akan memiliki salah satu bentuk:
I
I<A₁, ..., Aₓ>
di mana:
-
I
adalah pengidentifikasi tunggal; dan -
<A₁, ..., Aₓ>
adalah type_argument_list, ketika tidak ada type_argument_list yang ditentukan, pertimbangkanx
untuk menjadi nol.
R₀
ditentukan sebagai berikut:
- Jika
x
nol dan namespace_or_type_name muncul dalam deklarasi metode generik (§15,6) tetapi di luar atributheader metodenya, dan jika deklarasi tersebut menyertakan parameter jenis (§15.2.3) dengan namaI
, makaR₀
mengacu pada parameter jenis tersebut. - Jika tidak, jika namespace_or_type_name muncul dalam deklarasi jenis, maka untuk setiap jenis
T
instans (§15.3.2), dimulai dengan jenis instans dari deklarasi jenis tersebut dan melanjutkan dengan jenis instans setiap kelas yang mencakup atau deklarasi struktur (jika ada):- Jika
x
nol dan deklarasiT
menyertakan parameter jenis dengan namaI
, makaR₀
mengacu pada parameter jenis tersebut. - Jika tidak, jika namespace_or_type_name muncul dalam isi deklarasi jenis, dan
T
atau salah satu jenis dasarnya mengandung jenis bersarang yang dapat diakses dengan namaI
dan parameter jenisx
, makaR₀
mengacu pada jenis tersebut yang dibuat dengan argumen jenis yang diberikan. Jika ada lebih dari satu jenis tersebut, jenis yang dideklarasikan dalam jenis yang lebih turunan dipilih.Catatan: Anggota non-jenis (konstanta, bidang, metode, properti, pengindeks, operator, konstruktor instans, finalizer, dan konstruktor statis) dan jenis anggota dengan jumlah parameter jenis yang berbeda diabaikan saat menentukan arti namespace_or_type_name. catatan akhir
- Jika tidak, untuk setiap namespace
N
, dimulai dengan namespace layanan tempat namespace_or_type_name terjadi, berlanjut dengan setiap namespace enclosing (jika ada), dan diakhiri dengan namespace global, langkah-langkah berikut dievaluasi hingga entitas berada:- Jika
x
nol danI
merupakan nama namespace layanan diN
, maka:- Jika lokasi tempat namespace_or_type_name terjadi diapit oleh deklarasi namespace untuk dan deklarasi
N
namespace berisi extern_alias_directive atau using_alias_directive yang mengaitkan namaI
dengan namespace atau jenis, maka namespace_or_type_name ambigu dan terjadi kesalahan waktu kompilasi. - Jika tidak,
R₀
mengacu pada namespace bernamaI
dalamN
.
- Jika lokasi tempat namespace_or_type_name terjadi diapit oleh deklarasi namespace untuk dan deklarasi
- Jika tidak, jika
N
berisi jenis yang dapat diakses yang memiliki parameter namaI
danx
jenis, maka:- Jika
x
nol dan lokasi tempat namespace_or_type_name terjadi diapit oleh deklarasi namespace untukN
dan deklarasi namespace berisi extern_alias_directive atau using_alias_directive yang mengaitkan namaI
dengan namespace atau jenis, maka namespace_or_type_name ambigu dan terjadi kesalahan waktu kompilasi. - Jika tidak,
R₀
mengacu pada tipe yang dibentuk dengan argumen tipe yang diberikan.
- Jika
- Jika tidak, jika lokasi tempat namespace_or_type_name terjadi diapit oleh deklarasi namespace untuk
N
:- Jika
x
nol dan deklarasi namespace berisi extern_alias_directive atau using_alias_directive yang mengaitkan namaI
dengan namespace atau jenis yang diimpor, makaR₀
merujuk ke namespace atau jenis tersebut. - Sebaliknya, jika namespaces yang diimpor oleh using_namespace_directive deklarasi namespace mengandung tepat satu jenis dengan nama
I
dan parameter tipex
, makaR₀
merujuk pada jenis tersebut yang dibangun dengan argumen jenis yang diberikan. - Jika tidak, jika namespace yang diimpor oleh using_namespace_directive deklarasi namespace berisi lebih dari satu jenis yang memiliki nama tipe
I
dan parameter tipex
, maka namespace_or_type_name ambigu dan terjadi kesalahan waktu kompilasi.
- Jika
- Jika
- Jika tidak, namespace_or_type_name tidak terdefinisi dan terjadi kesalahan waktu kompilasi.
- Jika
Jika R₀
berhasil diselesaikan, bagian berikutnya dari namespace_or_type_name diselesaikan. Fragmen tata bahasa berikutnya terdiri dari pengulangan k ≥ 0
, dengan setiap pengulangan lebih lanjut menyelesaikan namespace atau jenis yang direferensikan.
Jika k
nol, yaitu tidak ada bagian berikutnya, maka namespace_or_type_name diselesaikan ke R₀
.
Jika tidak, setiap pengulangan akan memiliki salah satu bentuk:
.I
.I<A₁, ..., Aₓ>
di mana I
, A
dan x
didefinisikan seperti di atas.
Untuk setiap pengulangan n
, di mana 1 ≤ n ≤ k
, resolusinya, Rₙ
; yang melibatkan Rₚ
, di mana p = n - 1
, resolusi pengulangan sebelumnya; ditentukan sebagai berikut:
- Jika
x
nol danRₚ
merujuk ke namespace layanan danRₚ
berisi namespace berlapis dengan namaI
, makaRₙ
merujuk ke namespace layanan berlapis tersebut. - Jika tidak, jika
Rₚ
merujuk ke namespace danRₚ
berisi jenis yang dapat diakses yang memiliki namaI
dan parameter jenisx
, makaRₙ
merujuk pada jenis yang dibangun dengan argumen jenis yang diberikan. - Jika tidak, jika
Rₚ
mengacu pada kelas (mungkin dibangun) atau tipe struct danRₚ
atau salah satu kelas dasarnya berisi tipe berlapis yang dapat diakses yang memiliki namaI
dan parameter tipex
, makaRₙ
mengacu pada tipe tersebut yang dibangun dengan argumen tipe yang diberikan. Jika ada lebih dari satu jenis tersebut, jenis yang dideklarasikan dalam jenis yang lebih turunan dipilih.Catatan: Jika arti dari
T.I
, untuk tipe tertentuT
, sedang ditentukan dalam rangka menyelesaikan spesifikasi kelas dasar dariT
, maka kelas dasar langsung dariT
dianggap sebagaiobject
(§15.2.4.2). catatan akhir - Jika tidak, namespace_or_type_name tidak valid dan terjadi kesalahan waktu kompilasi.
Resolusi namespace_or_type_name adalah resolusi pengulangan akhir, Rₖ
.
namespace_or_type_name diizinkan untuk mereferensikan kelas statis (§15.2.2.4) hanya jika
-
namespace_or_type_name adalah
T
dalam namespace_or_type_name formulirT.I
, atau -
namespace_or_type_name adalah
T
dalam typeof_expression (§12.8.18) dengan bentuktypeof(T)
7.8.2 Nama yang tidak memenuhi syarat
Setiap deklarasi namespace dan deklarasi jenis memiliki nama yang tidak memenuhi syarat yang ditentukan sebagai berikut:
- Untuk deklarasi namespace layanan, nama yang tidak memenuhi syarat adalah qualified_identifier yang ditentukan dalam deklarasi.
- Untuk deklarasi jenis tanpa type_parameter_list, nama yang tidak memenuhi syarat adalah pengidentifikasi yang ditentukan dalam deklarasi.
- Untuk deklarasi tipe dengan parameter tipe K, nama yang tidak memenuhi syarat adalah pengidentifikasi yang ditentukan dalam deklarasi, diikuti oleh spesifier_dimensi_generic (§12.8.18) untuk parameter tipe K.
7.8.3 Nama yang sepenuhnya memenuhi syarat
Setiap namespace dan deklarasi jenis memiliki nama yang sepenuhnya memenuhi syarat, yang secara unik mengidentifikasi namespace atau deklarasi jenis di antara semua lainnya dalam program. Nama namespace layanan yang sepenuhnya memenuhi syarat atau deklarasi jenis dengan nama N
yang tidak memenuhi syarat ditentukan sebagai berikut:
- Jika
N
adalah anggota namespace layanan global, nama yang sepenuhnya memenuhi syarat adalahN
. - Jika tidak, nama yang sepenuhnya memenuhi syarat adalah
S.N
, di manaS
adalah nama namespace layanan yang sepenuhnya memenuhi syarat atau deklarasi jenis di manaN
dideklarasikan.
Dengan kata lain, nama N
yang sepenuhnya memenuhi syarat adalah jalur hierarkis lengkap pengidentifikasi dan generic_dimension_specifieryang mengarah ke N
, mulai dari namespace layanan global. Karena setiap anggota namespace layanan atau jenis harus memiliki nama yang unik, itu mengikuti bahwa nama namespace layanan yang sepenuhnya memenuhi syarat atau deklarasi jenis selalu unik. Ini adalah kesalahan waktu kompilasi untuk nama yang sepenuhnya memenuhi syarat yang sama untuk merujuk ke dua entitas yang berbeda. Secara khusus:
- Ini adalah kesalahan untuk deklarasi namespace dan deklarasi jenis untuk memiliki nama yang sepenuhnya memenuhi syarat yang sama.
- Ini adalah kesalahan untuk dua jenis deklarasi jenis yang berbeda untuk memiliki nama yang sepenuhnya memenuhi syarat yang sama (misalnya, jika struct dan deklarasi kelas memiliki nama yang sepenuhnya memenuhi syarat).
- Ini adalah kesalahan untuk deklarasi jenis tanpa pengubah parsial untuk memiliki nama yang sepenuhnya memenuhi syarat yang sama dengan deklarasi jenis lain (§15.2.7).
Contoh: Contoh di bawah ini menunjukkan beberapa namespace layanan dan deklarasi jenis bersama dengan nama terkait yang sepenuhnya memenuhi syarat.
class A {} // A namespace X // X { class B // X.B { class C {} // X.B.C } namespace Y // X.Y { class D {} // X.Y.D } } namespace X.Y // X.Y { class E {} // X.Y.E class G<T> // X.Y.G<> { class H {} // X.Y.G<>.H } class G<S,T> // X.Y.G<,> { class H<U> {} // X.Y.G<,>.H<> } }
contoh akhir
7.9 Manajemen memori otomatis
C# menggunakan manajemen memori otomatis, yang membebaskan pengembang dari mengalokasikan dan membebaskan memori yang ditempati oleh objek secara manual. Kebijakan manajemen memori otomatis diterapkan oleh pengumpul sampah. Siklus hidup manajemen memori objek adalah sebagai berikut:
- Ketika objek dibuat, memori dialokasikan untuk itu, konstruktor dijalankan, dan objek dianggap langsung.
- Jika tidak ada objek maupun bidang instansnya yang dapat diakses oleh kemungkinan kelanjutan eksekusi, selain berjalannya finalizer, objek dianggap tidak lagi digunakan dan memenuhi syarat untuk finalisasi.
Catatan: Pengkompilasi C# dan pengumpul sampah mungkin memilih untuk menganalisis kode untuk menentukan referensi mana ke objek yang mungkin digunakan di masa mendatang. Misalnya, jika variabel lokal yang berada dalam cakupan adalah satu-satunya referensi yang ada ke objek, tetapi variabel lokal itu tidak pernah disebut dalam kemungkinan kelanjutan eksekusi dari titik eksekusi saat ini dalam prosedur, pengumpul sampah mungkin (tetapi tidak diperlukan untuk) memperlakukan objek sebagai tidak lagi digunakan. catatan akhir
- Setelah objek memenuhi syarat untuk finalisasi, pada beberapa waktu kemudian yang tidak ditentukan finalizer (§15.13) (jika ada) untuk objek dijalankan. Dalam keadaan normal, finalizer untuk objek dijalankan sekali saja, meskipun API yang ditentukan implementasi dapat memungkinkan perilaku ini ditimpa.
- Setelah finalizer untuk objek dijalankan, jika tidak ada objek atau bidang instansnya yang dapat diakses oleh kemungkinan kelanjutan eksekusi, termasuk berjalannya finalizer, objek dianggap tidak dapat diakses dan objek menjadi memenuhi syarat untuk pengumpulan.
Catatan: Objek yang sebelumnya tidak dapat diakses dapat diakses lagi karena finalizer-nya. Contoh ini disediakan di bawah ini. catatan akhir
- Akhirnya, pada beberapa waktu setelah objek memenuhi syarat untuk pengumpulan, pengumpul sampah membebaskan memori yang terkait dengan objek tersebut.
Pengumpul sampah mempertahankan informasi tentang penggunaan objek, dan menggunakan informasi ini untuk membuat keputusan manajemen memori, seperti di mana dalam memori untuk menemukan objek yang baru dibuat, kapan harus merelokasi objek, dan ketika objek tidak lagi digunakan atau tidak dapat diakses.
Seperti bahasa lain yang mengasumsikan keberadaan pengumpul sampah, C# dirancang agar pengumpul sampah dapat menerapkan berbagai kebijakan manajemen memori. C# tidak menentukan batasan waktu dalam rentang tersebut, atau urutan di mana finalizer dijalankan. Apakah finalizer dijalankan sebagai bagian dari penghentian aplikasi ditentukan implementasi (§7.2).
Perilaku pengumpul sampah dapat dikontrol, hingga beberapa derajat, melalui metode statis pada kelas System.GC
. Kelas ini dapat digunakan untuk meminta koleksi terjadi, finalizer untuk dijalankan (atau tidak dijalankan), dan sebagainya.
Contoh: Karena pengumpul sampah diizinkan lintang lebar dalam memutuskan kapan harus mengumpulkan objek dan menjalankan finalizer, implementasi yang sesuai dapat menghasilkan output yang berbeda dari yang ditunjukkan oleh kode berikut. Program ini
class A { ~A() { Console.WriteLine("Finalize instance of A"); } } class B { object Ref; public B(object o) { Ref = o; } ~B() { Console.WriteLine("Finalize instance of B"); } } class Test { static void Main() { B b = new B(new A()); b = null; GC.Collect(); GC.WaitForPendingFinalizers(); } }
membuat instans kelas
A
dan instans kelasB
. Objek-objek ini menjadi memenuhi syarat untuk pengumpulan sampah ketika variabelb
diberi nilainull
, karena setelah waktu ini tidak mungkin bagi kode yang ditulis pengguna untuk mengaksesnya. Outputnya bisa berupaFinalize instance of A Finalize instance of B
atau
Finalize instance of B Finalize instance of A
karena bahasa tidak memberlakukan batasan pada urutan pengumpulan sampah objek.
Dalam kasus yang halus, perbedaan antara "memenuhi syarat untuk finalisasi" dan "memenuhi syarat untuk pengumpulan" bisa menjadi penting. Contohnya,
class A { ~A() { Console.WriteLine("Finalize instance of A"); } public void F() { Console.WriteLine("A.F"); Test.RefA = this; } } class B { public A Ref; ~B() { Console.WriteLine("Finalize instance of B"); Ref.F(); } } class Test { public static A RefA; public static B RefB; static void Main() { RefB = new B(); RefA = new A(); RefB.Ref = RefA; RefB = null; RefA = null; // A and B now eligible for finalization GC.Collect(); GC.WaitForPendingFinalizers(); // B now eligible for collection, but A is not if (RefA != null) { Console.WriteLine("RefA is not null"); } } }
Dalam program di atas, jika pengumpul sampah memilih untuk menjalankan finalizer
A
sebelum finalizerB
, maka output program ini mungkin:Finalize instance of A Finalize instance of B A.F RefA is not null
Perhatikan bahwa meskipun instans
A
tidak digunakan danA
finalizer dijalankan, masih mungkin untuk metodeA
(dalam hal ini,F
) untuk dipanggil dari finalizer lain. Selain itu, perhatikan bahwa menjalankan finalizer dapat menyebabkan objek dapat digunakan dari program utama lagi. Dalam hal ini, berjalannyaB
finalizer menyebabkan instansA
yang sebelumnya tidak digunakan, untuk dapat diakses dari referensiTest.RefA
langsung . Setelah panggilan keWaitForPendingFinalizers
, instansB
memenuhi syarat untuk koleksi, tetapi instansnyaA
tidak, karena referensiTest.RefA
.contoh akhir
7.10 Urutan eksekusi
Eksekusi program C# berlanjut sewaktu-waktu sehingga efek samping dari setiap utas yang dieksekusi dipertahankan pada titik eksekusi kritis.
Efek samping didefinisikan sebagai baca atau tulis bidang volatil, tulis ke variabel non-volatil, tulis ke sumber daya eksternal, dan pelemparan pengecualian. Titik eksekusi kritis di mana urutan efek samping ini harus dipertahankan adalah referensi ke bidang volatil (§15.5.4), lock
pernyataan (§13.13), dan pembuatan dan penghentian utas. Lingkungan eksekusi bebas untuk mengubah urutan eksekusi program C#, tunduk pada batasan berikut:
- Ketergantungan data dipertahankan dalam utas eksekusi. Artinya, nilai setiap variabel dihitung seolah-olah semua pernyataan dalam utas dijalankan dalam urutan program asli.
- Aturan pemesanan inisialisasi dipertahankan (§15.5.5, §15.5.6).
- Urutan efek samping dipertahankan sehubungan dengan pembacaan dan penulisan volatil (§15.5.4). Selain itu, lingkungan eksekusi tidak perlu mengevaluasi bagian dari ekspresi jika dapat menyimpulkan bahwa nilai ekspresi tidak digunakan dan bahwa tidak ada efek samping yang diperlukan yang dihasilkan (termasuk yang disebabkan oleh memanggil metode atau mengakses bidang volatil). Ketika eksekusi program terganggu oleh peristiwa asinkron (seperti pengecualian yang dilemparkan oleh utas lain), tidak dijamin bahwa efek samping yang dapat diamati terlihat dalam urutan program asli.
ECMA C# draft specification