Bagikan melalui


7 Konsep dasar

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 adalah System.Threading.Tasks.Task atau System.Threading.Tasks.Task<int>.
  • Jenis pengembaliannya adalah void, int, System.Threading.Tasks.Task, atau System.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. Metode async void , atau metode yang async mengembalikan jenis yang dapat ditunggu yang berbeda seperti ValueTask atau ValueTask<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 adalah System.Threading.Tasks.Task, jenis pengembalian metode yang disintesis adalah void
  • Jika jenis Main pengembalian metode adalah System.Threading.Tasks.Task<int>, jenis pengembalian metode yang disintesis adalah int

Eksekusi metode yang disintesis berlanjut sebagai berikut:

  • Metode yang disintesis memanggil Main metode , meneruskan nilai parameternya string[] sebagai argumen jika metode memiliki parameter seperti itu Main .
  • 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 jenis System.Threading.Tasks.Task<int>pengembalian , jika tugas berhasil diselesaikan, int nilai yang dikembalikan oleh GetResult() 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 nullawal , 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.SuppressFinalizepustaka , 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. Variabel z 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 dan Megacorp.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 dan G mengakibatkan kesalahan waktu kompilasi karena nama i dideklarasikan di blok luar dan tidak dapat dideklarasikan ulang di blok dalam. Namun, H metode dan I berlaku karena keduanya idinyatakan 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 objectdasar 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 kelas object 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 dari public adalah "akses tidak terbatas".
  • Dilindungi, yang dipilih dengan menyertakan pengubah protected dalam deklarasi anggota. Arti intuitif adalah protected "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 dari internal adalah "akses terbatas pada perakitan ini".
  • Internal terlindungi, yang dipilih dengan menyertakan dan protected pengubah internal dalam deklarasi anggota. Arti intuitif adalah protected internal "dapat diakses dalam rakitan ini serta jenis yang berasal dari kelas yang berisi".
  • Dilindungi privat, yang dipilih dengan menyertakan dan privateprotected pengubah dalam deklarasi anggota. Arti intuitif adalah private 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 dari private 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 atau internal mendeklarasikan aksesibilitas dan default ke internal 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 atau internal mendeklarasikan aksesibilitas. catatan akhir

  • Anggota struct dapat memiliki publicaksesibilitas , internal, atau private dinyatakan dan default ke private aksesibilitas yang dinyatakan karena struktur secara implisit disegel. Anggota struct yang diperkenalkan dalam struct (yaitu, tidak diwariskan oleh struktur tersebut) tidak dapat memiliki protectedaksesibilitas , , protected internalatau private protected dinyatakan.

    Catatan: Jenis yang dinyatakan sebagai anggota struktur dapat memiliki publicaksesibilitas , , internalatau private dinyatakan, sedangkan jenis yang dinyatakan sebagai anggota namespace hanya public dapat memiliki atau internal 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, , intatau 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, domain T aksesibilitas adalah teks P program dan program apa pun yang mereferensikan P.
  • Jika aksesibilitas T yang dinyatakan bersifat internal, domain T 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 adalah public, domain aksesibilitas M adalah domain aksesibilitas T.
  • Jika aksesibilitas M yang dinyatakan adalah protected internal, mari kita D menjadi persatuan teks P program dan teks program dari jenis apa pun yang berasal dari T, yang dideklarasikan di luar P. Domain aksesibilitas M adalah persimpangan domain T aksesibilitas dengan D.
  • Jika aksesibilitas M yang dinyatakan adalah private protected, biarkan D menjadi persimpangan teks P program dan teks program dari T dan jenis apa pun yang berasal dari T. Domain aksesibilitas M adalah persimpangan domain T aksesibilitas dengan D.
  • Jika aksesibilitas M yang dinyatakan adalah protected, mari kita D menjadi gabungan teks Tprogram dan teks program dari jenis apa pun yang berasal dari T. Domain aksesibilitas M adalah persimpangan domain T aksesibilitas dengan D.
  • Jika aksesibilitas yang dideklarasikan M adalah internal, domain aksesibilitas M adalah persimpangan domain aksesibilitas T dengan teks program P.
  • Jika aksesibilitas yang dideklarasikan M adalah private, domain aksesibilitas M adalah teks program T.

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 adalah public, akses diizinkan.
  • Jika tidak, jika M adalah protected internal, akses diizinkan jika terjadi dalam program yang M dinyatakan, atau jika terjadi dalam kelas yang berasal dari kelas yang M dideklarasikan dan berlangsung melalui jenis kelas turunan (§7.5.4).
  • Jika tidak, jika M adalah protected, akses diizinkan jika terjadi dalam kelas yang M dinyatakan, atau jika terjadi dalam kelas yang berasal dari kelas yang M dideklarasikan dan berlangsung melalui jenis kelas turunan (§7.5.4).
  • Jika tidak, jika M adalah internal, akses diizinkan jika terjadi dalam program di mana M dinyatakan.
  • Jika tidak, jika M adalah private, akses diizinkan jika terjadi dalam jenis yang dinyatakan M .
  • 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 dan A.X tidak terbatas.
  • Domain aksesibilitas dari A.Y, , B, B.XB.Y, B.C, B.C.X, , dan B.C.Y adalah teks program dari program yang berisi.
  • Domain aksesibilitas adalah A.Z teks program dari A.
  • Domain aksesibilitas B.Z dan B.D adalah teks program dari B, termasuk teks program dan B.CB.D.
  • Domain aksesibilitas adalah B.C.Z teks program dari B.C.
  • Domain aksesibilitas B.D.X dan B.D.Y adalah teks program dari B, termasuk teks program dan B.CB.D.
  • Domain aksesibilitas adalah B.D.Z teks program dari B.D. Seperti yang diilustrasikan contoh, domain aksesibilitas anggota tidak pernah lebih besar dari jenis yang berisi. Misalnya, meskipun semua X anggota memiliki aksesibilitas yang dinyatakan publik, semua tetapi A.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 anggota x privat dari A kelas . Karena anggota bersifat pribadi, anggota hanya dapat diakses dalam class_body .A Dengan demikian, akses untuk b.x berhasil dalam A.F metode , tetapi gagal dalam B.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 Minstans 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 dari E, di mana T adalah kelas T, atau jenis kelas yang dibangun dari T.DD
  • 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 mengakses x melalui instans dan AB , karena dalam kedua kasus akses berlangsung melalui instans A atau kelas yang berasal dari A. Namun, dalam B, tidak mungkin untuk mengakses x melalui instans A, karena A tidak berasal dari B.

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 anggota C<int>.x di D valid meskipun kelas D berasal dari C<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 Maksesibilitas . Dengan kata lain, T setidaknya dapat diakses seolah-olah MT 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 karena A setidaknya tidak dapat diakses sebagai B.

contoh akhir

Contoh: Demikian juga, dalam kode berikut

class A {...}

public class B
{
    A F() {...}
    internal A G() {...}
    public A H() {...}
}

H metode dalam B menghasilkan kesalahan waktu kompilasi karena jenis A 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 atau this , 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 inpengubah 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, , outdan ref. 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 pun out(ref) adalah bagian dari tanda tangan. Dengan demikian, F(int), F(in int), F(out int) , dan F(ref int) semua tanda tangan unik. Namun, F(in int), F(out int) , dan F(ref int) tidak dapat dideklarasikan dalam antarmuka yang sama karena tanda tangan mereka hanya berbeda dengan in, , outdan ref. Selain itu, perhatikan bahwa jenis pengembalian dan params pengubah bukan bagian dari tanda tangan, sehingga tidak dimungkinkan untuk membebani hanya berdasarkan jenis pengembalian atau pada penyertaan atau pengecualian pengubah params . Dengan demikian, deklarasi metode F(int) dan F(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.NN

  • 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_blockswitch pernyataan (§13.8.3) adalah switch_block.

  • Cakupan variabel lokal yang dideklarasikan dalam for_initializerfor 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 merujuk i 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 untuk i 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 , penggunaan j dalam penginisialisasi untuk deklarasi j 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 variabel A lokal dan dalam konteks jenis untuk merujuk ke kelas A.

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 , variabel i instans disembunyikan oleh variabel ilokal , tetapi dalam G metode , i masih mengacu pada variabel instans. Di dalam fungsi M1float i lokal menyembunyikan langsung-luar i. Parameter i lambda menyembunyikan bagian float 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) memanggil F yang dideklarasikan karena Inner semua kejadian F luar disembunyikan oleh deklarasi bagian dalam. Untuk alasan yang sama, panggilan F("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 dalam Derived 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 versi Base yang lebih baru memperkenalkan F 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 bahwa F in Derived 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 baru Derived memiliki F akses privat, cakupannya tidak meluas ke Base.FDerivedMoreDerived Dengan demikian, panggilan F() masuk MoreDerived.G valid dan akan memanggil Base.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, pertimbangkan x 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 nama I, maka R₀ 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 deklarasi T menyertakan parameter jenis dengan nama I, maka R₀ 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 nama I dan parameter jenis x, maka R₀ 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 dan I merupakan nama namespace layanan di N, 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 nama I dengan namespace atau jenis, maka namespace_or_type_name ambigu dan terjadi kesalahan waktu kompilasi.
        • Jika tidak, R₀ mengacu pada namespace bernama I dalam N.
      • Jika tidak, jika N berisi jenis yang dapat diakses yang memiliki parameter nama I dan x jenis, maka:
        • Jika x nol dan lokasi tempat namespace_or_type_name terjadi diapit oleh deklarasi namespace untuk N dan deklarasi namespace berisi extern_alias_directive atau using_alias_directive yang mengaitkan nama I 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 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 nama I dengan namespace atau jenis yang diimpor, maka R₀ 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 tipe x, maka R₀ 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 tipe x, maka namespace_or_type_name ambigu dan terjadi kesalahan waktu kompilasi.
    • Jika tidak, namespace_or_type_name tidak terdefinisi dan terjadi kesalahan waktu kompilasi.

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 dan Rₚ merujuk ke namespace layanan dan Rₚ berisi namespace berlapis dengan nama I, maka Rₙ merujuk ke namespace layanan berlapis tersebut.
  • Jika tidak, jika Rₚ merujuk ke namespace dan Rₚ berisi jenis yang dapat diakses yang memiliki nama I dan parameter jenis x, maka Rₙ merujuk pada jenis yang dibangun dengan argumen jenis yang diberikan.
  • Jika tidak, jika Rₚ mengacu pada kelas (mungkin dibangun) atau tipe struct dan Rₚ atau salah satu kelas dasarnya berisi tipe berlapis yang dapat diakses yang memiliki nama I dan parameter tipe x, maka Rₙ 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 tertentu T, sedang ditentukan dalam rangka menyelesaikan spesifikasi kelas dasar dari T, maka kelas dasar langsung dari T dianggap sebagai object (§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 formulir T.I, atau
  • namespace_or_type_name adalah T dalam typeof_expression (§12.8.18) dengan bentuk typeof(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 adalah N.
  • Jika tidak, nama yang sepenuhnya memenuhi syarat adalah S.N, di mana S adalah nama namespace layanan yang sepenuhnya memenuhi syarat atau deklarasi jenis di mana N 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:

  1. Ketika objek dibuat, memori dialokasikan untuk itu, konstruktor dijalankan, dan objek dianggap langsung.
  2. 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

  3. 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.
  4. 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

  5. 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 kelas B. Objek-objek ini menjadi memenuhi syarat untuk pengumpulan sampah ketika variabel b diberi nilai null, karena setelah waktu ini tidak mungkin bagi kode yang ditulis pengguna untuk mengaksesnya. Outputnya bisa berupa

Finalize 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 finalizer B, 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 dan Afinalizer dijalankan, masih mungkin untuk metode A (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, berjalannya Bfinalizer menyebabkan instans A yang sebelumnya tidak digunakan, untuk dapat diakses dari referensi Test.RefAlangsung . Setelah panggilan ke WaitForPendingFinalizers, instans B memenuhi syarat untuk koleksi, tetapi instansnya A tidak, karena referensi Test.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.