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.
19.1 Umum
Antarmuka mendefinisikan kontrak. Kelas atau struktur yang mengimplementasikan antarmuka harus mematuhi kontraknya. Antarmuka dapat mewarisi dari beberapa antarmuka dasar, dan kelas atau struktur dapat mengimplementasikan beberapa antarmuka.
Antarmuka mungkin berisi berbagai jenis anggota, seperti yang dijelaskan dalam §19.4. Antarmuka itu sendiri dapat memberikan implementasi untuk beberapa atau semua anggota fungsi yang dinyatakannya. Anggota yang antarmukanya tidak menyediakan implementasi abstrak. Implementasinya harus disediakan oleh kelas atau struktur yang mengimplementasikan antarmuka, atau antarmuka turunan yang memberikan definisi penimpaan.
Catatan: Secara historis, menambahkan anggota fungsi baru ke antarmuka berdampak pada semua konsumen yang ada dari jenis antarmuka tersebut; itu adalah perubahan yang melanggar. Penambahan implementasi anggota fungsi antarmuka memungkinkan pengembang untuk meningkatkan antarmuka sambil tetap memungkinkan pelaksana apa pun untuk mengambil alih implementasi tersebut. Pengguna antarmuka dapat menerima implementasi sebagai perubahan yang tidak melanggar; namun, jika persyaratannya berbeda, mereka dapat mengambil alih implementasi yang disediakan. catatan akhir
19.2 Deklarasi antarmuka
19.2.1 Umum
interface_declaration adalah type_declaration (§14,7) yang mendeklarasikan jenis antarmuka baru.
interface_declaration
: attributes? interface_modifier* 'partial'? 'interface'
identifier variant_type_parameter_list? interface_base?
type_parameter_constraints_clause* interface_body ';'?
;
Interface_declaration terdiri dari sekumpulan atribut opsional (§23), diikuti oleh sekumpulan interface_modifieropsional (§19.2.2), diikuti oleh pengubah parsial opsional (§15.2.7), diikuti oleh kata kunci interface dan pengidentifikasi yang menamai antarmuka, diikuti dengan spesifikasi variant_type_parameter_list opsional (§19.2.3), diikuti dengan spesifikasi interface_base opsional (§19.2.4), diikuti dengan spesifikasi type_parameter_constraints_clauseopsional (§15.2.5), diikuti dengan interface_body (§19,3), secara opsional diikuti dengan titik koma.
Deklarasi antarmuka tidak boleh menyediakan type_parameter_constraints_clausekecuali juga menyediakan variant_type_parameter_list.
Deklarasi antarmuka yang menyediakan variant_type_parameter_list adalah deklarasi antarmuka generik. Selain itu, antarmuka apa pun yang ada di dalam deklarasi kelas generik atau deklarasi struct generik adalah deklarasi antarmuka generik, karena argumen tipe untuk tipe yang berisi harus disediakan untuk membuat tipe yang telah dibangun (§8.4).
19.2.2 Pengubah antarmuka
Interface_declaration dapat secara opsional menyertakan urutan pengubah antarmuka:
interface_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
unsafe_modifier (§24.2) hanya tersedia dalam kode tidak aman (§24).
Ini adalah kesalahan pada waktu kompilasi jika pengubah yang sama muncul beberapa kali dalam deklarasi antarmuka.
Pengubah new hanya diizinkan pada antarmuka yang ditentukan dalam kelas. Ini menentukan bahwa antarmuka menyembunyikan anggota yang diwariskan dengan nama yang sama, seperti yang dijelaskan dalam §15.3.5.
Pengubah public, protected, internal, dan private mengontrol aksesibilitas antarmuka. Tergantung pada konteks di mana deklarasi antarmuka terjadi, hanya beberapa pengubah ini yang mungkin diizinkan (§7.5.2). Ketika deklarasi jenis parsial (§15.2.7) menyertakan spesifikasi aksesibilitas (melalui publicpengubah , , protected, internaldan private ), aturan dalam §15.2.2 berlaku.
19.2.3 Daftar parameter jenis varian
19.2.3.1 Umum
Daftar parameter tipe varian hanya dapat terjadi pada tipe antarmuka dan delegasi. Perbedaan dari type_parameter_list biasa adalah variance_annotation opsional pada setiap parameter jenis.
variant_type_parameter_list
: '<' variant_type_parameter (',' variant_type_parameter)* '>'
;
variant_type_parameter
: attributes? variance_annotation? type_parameter
;
variance_annotation
: 'in'
| 'out'
;
Jika anotasi varians adalah out, parameter jenis disebut sebagai kovarian. Jika anotasi varians adalah in, parameter jenis dikatakan kontravarian. Jika tidak ada anotasi varians, parameter jenis dikatakan invarian.
Contoh: Dalam hal berikut:
interface C<out X, in Y, Z> { X M(Y y); Z P { get; set; } }
Xkovarian,Ykontravarian danZinvarian.contoh akhir
Jika antarmuka generik dideklarasikan dalam beberapa bagian (§15.2.3), setiap deklarasi parsial harus menentukan varians yang sama untuk setiap parameter jenis.
19.2.3.2 Keamanan varians
Terjadinya anotasi varians dalam daftar parameter jenis membatasi tempat di mana jenis dapat terjadi dalam deklarasi jenis.
Jenis T tidak aman untuk output jika salah satu kondisi berikut terpenuhi:
-
Tadalah parameter jenis kontravarian -
Tadalah jenis array dengan jenis elemen yang tidak aman untuk keluaran -
Tadalah antarmuka atauSᵢ,... Aₑtipe delegasi yang dibuat dari tipe generikS<Xᵢ, ... Xₑ>di mana untuk setidaknya satuAᵢ, salah satu dari kondisi berikut terpenuhi:-
Xᵢkovarian atau invarian danAᵢoutput tidak aman. -
Xᵢbersifat kontravarian atau invarian danAᵢtidak aman untuk input.
-
Jenis T tidak aman untuk input jika salah satu hal berikut ini berlaku:
-
Tadalah parameter tipe kovarian -
Tadalah jenis array dengan jenis elemen yang tidak aman untuk input -
Tadalah antarmuka atauS<Aᵢ,... Aₑ>tipe delegasi yang dibuat dari tipe generikS<Xᵢ, ... Xₑ>di mana untuk setidaknya satuAᵢ, salah satu dari kondisi berikut terpenuhi:-
Xᵢkovarian atau invarian danAᵢtidak aman input. -
Xᵢadalah kontravarian atau invarian danAᵢtidak aman untuk keluaran.
-
Secara intuitif, jenis yang tidak aman untuk output dilarang dalam posisi output, dan jenis yang tidak aman untuk input dilarang dalam posisi input.
Jenis adalah output-safe jika tidak output-unsafe, dan input-safe jika tidak input-unsafe.
19.2.3.3 Konversi varians
Tujuan anotasi varians adalah untuk menyediakan konversi yang lebih fleksibel (tetapi masih aman untuk tipe) ke tipe antarmuka dan delegasi. Untuk ini, definisi implisit (§10,2) dan konversi eksplisit (§10,3) menggunakan gagasan konvertibilitas varians, yang didefinisikan sebagai berikut:
Jenis T<Aᵢ, ..., Aᵥ> dapat dikonversi varians ke jenis T<Bᵢ, ..., Bᵥ> jika T merupakan antarmuka atau jenis delegasi yang dideklarasikan dengan parameter jenis varian T<Xᵢ, ..., Xᵥ>, dan untuk setiap parameter jenis varian Xᵢ, salah satu dari kondisi berikut harus terpenuhi:
-
Xᵢbersifat kovarian dan terdapat konversi referensi implisit atau identitas dariAᵢkeBᵢ -
Xᵢadalah kontravarian dan terdapat referensi implisit atau konversi identitas dariBᵢkeAᵢ. -
Xᵢinvariant dan konversi identitas ada dariAᵢkeBᵢ
19.2.4 Antarmuka dasar
Antarmuka dapat mewarisi dari nol atau lebih jenis antarmuka, yang disebut antarmuka dasar eksplisitdari antarmuka. Ketika antarmuka memiliki satu atau beberapa antarmuka dasar eksplisit, maka dalam deklarasi antarmuka tersebut, pengidentifikasi antarmuka diikuti oleh titik dua dan daftar jenis antarmuka dasar yang dipisahkan koma.
Antarmuka turunan dapat menyatakan anggota baru yang menyembunyikan anggota yang diwariskan (§7.7.2.3) yang dideklarasikan dalam antarmuka dasar atau secara eksplisit menerapkan anggota yang diwariskan (§19.6.2) yang dideklarasikan dalam antarmuka dasar.
interface_base
: ':' interface_type_list
;
Antarmuka dasar eksplisit dapat dibangun jenis antarmuka (§8.4, §19.2). Antarmuka dasar tidak dapat menjadi parameter jenis sendiri, meskipun dapat melibatkan parameter jenis yang berada dalam cakupan.
Untuk jenis antarmuka yang dibangun, antarmuka dasar eksplisit dibentuk dengan mengambil deklarasi antarmuka dasar eksplisit pada deklarasi jenis generik, lalu mengganti, untuk setiap type_parameter dalam deklarasi antarmuka dasar, type_argument yang sesuai dari jenis yang dibangun.
Antarmuka dasar eksplisit dari sebuah antarmuka harus setidaknya seaksesibel antarmuka itu sendiri (§7.5.5).
Catatan: Misalnya, ini adalah kesalahan waktu kompilasi untuk menentukan antarmuka
privateatauinternaldi dalam interface_base dari antarmukapublic. catatan akhir
Ini adalah kesalahan waktu kompilasi bagi antarmuka untuk secara langsung atau tidak langsung mewarisi dari dirinya sendiri.
Antarmuka dasarantarmuka adalah antarmuka dasar eksplisit dan antarmuka dasarnya. Dengan kata lain, sekumpulan antarmuka dasar adalah penutupan transitif yang lengkap dari antarmuka dasar eksplisit, antarmuka dasar eksplisit mereka, dan sebagainya. Antarmuka mewarisi semua anggota antarmuka dasarnya.
Contoh: Dalam kode berikut
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } interface IComboBox: ITextBox, IListBox {}antarmuka
IComboBoxdasar adalahIControl,ITextBox, danIListBox. Dengan kata lain,IComboBoxantarmuka di atas mewarisi anggotaSetTextdanSetItemssertaPaint.contoh akhir
Anggota yang diwarisi dari tipe generik yang telah ditentukan akan diwarisi setelah penggantian tipe. Artinya, setiap tipe konstituen dalam anggota memiliki parameter tipe dalam deklarasi kelas dasar yang digantikan oleh argumen tipe yang sesuai yang digunakan dalam spesifikasi class_base.
Contoh: Dalam kode berikut
interface IBase<T> { T[] Combine(T a, T b); } interface IDerived : IBase<string[,]> { // Inherited: string[][,] Combine(string[,] a, string[,] b); }antarmuka
IDerivedmewarisi metodeCombinesetelah parameter jenisTdiganti denganstring[,].contoh akhir
Kelas atau struktur yang mengimplementasikan antarmuka juga secara implisit mengimplementasikan semua antarmuka dasar antarmuka.
Penanganan antarmuka pada beberapa bagian dari deklarasi antarmuka parsial (§15.2.7) dibahas lebih lanjut dalam §15.2.4.3.
Setiap antarmuka dasar antarmuka harus aman output (§19.2.3.2).
19.3 Isi antarmuka
Interface_body antarmuka menentukan anggota antarmuka.
interface_body
: '{' interface_member_declaration* '}'
;
19.4 Anggota antarmuka
19.4.1 Umum
Anggota antarmuka adalah anggota yang diwarisi dari antarmuka dasar dan anggota yang dideklarasikan oleh antarmuka itu sendiri.
interface_member_declaration
: constant_declaration
| field_declaration
| method_declaration
| property_declaration
| event_declaration
| indexer_declaration
| static_constructor_declaration
| operator_declaration
| type_declaration
;
Klausa ini menambah deskripsi anggota di kelas (§15,3) dengan batasan untuk antarmuka. Anggota Antarmuka dinyatakan menggunakan member_declarationdengan aturan tambahan berikut:
- Finalizer_declaration tidak diperbolehkan.
- Konstruktor instans, constructor_declaration, tidak diizinkan.
- Semua anggota antarmuka secara implisit memiliki akses publik; namun, pengubah akses eksplisit (§7.5.2) diizinkan kecuali pada konstruktor statis (§15.12).
- Pengubah
abstracttersirat untuk anggota fungsi antarmuka tanpa badan; pengubah tersebut dapat diberikan secara eksplisit. - Anggota fungsi instans antarmuka yang deklarasinya menyertakan isi adalah anggota implisit
virtualkecuali pengubahsealedatauprivatedigunakan. Pengubahvirtualdapat diberikan secara eksplisit. - Anggota
privatefungsi atausealedantarmuka harus memiliki isi. - Anggota
privatefungsi tidak boleh memiliki pengubahsealed. - Antarmuka turunan dapat mengambil alih anggota abstrak atau virtual yang dideklarasikan dalam antarmuka dasar.
- Anggota fungsi yang diimplementasikan secara eksplisit tidak boleh memiliki pengubah
sealed.
Beberapa deklarasi, seperti constant_declaration (§15.4) tidak memiliki batasan dalam antarmuka.
Anggota antarmuka yang diwariskan secara khusus bukan bagian dari ruang deklarasi antarmuka. Dengan demikian, antarmuka diizinkan untuk mendeklarasikan anggota dengan nama atau tanda tangan yang sama dengan anggota yang diwariskan. Ketika ini terjadi, anggota antarmuka turunan dikatakan menyembunyikan anggota antarmuka dasar. Menyembunyikan anggota yang diwariskan tidak dianggap sebagai kesalahan, tetapi itu menghasilkan peringatan (§7.7.2.3).
Jika pengubah new disertakan dalam deklarasi yang tidak menyembunyikan anggota turunan, peringatan dikeluarkan sebagai akibatnya.
Catatan: Anggota di kelas
objecttidak, secara ketat berbicara, anggota antarmuka apa pun (§19.4). Namun, anggota di kelasobjecttersedia melalui pencarian anggota dalam jenis antarmuka apa pun (§12,5). catatan akhir
Kumpulan anggota antarmuka yang dideklarasikan dalam beberapa bagian (§15.2.7) adalah gabungan dari anggota yang dideklarasikan di setiap bagian. Badan semua bagian deklarasi antarmuka memiliki ruang deklarasi yang sama (§7,3), dan cakupan setiap anggota (§7,7) meluas ke badan semua bagian.
Contoh: Pertimbangkan antarmuka
IAdengan implementasi untuk anggotaMdan propertiP. JenisCpenerapan tidak menyediakan implementasi untukMatauP. Mereka harus diakses melalui referensi yang jenis waktu kompilasinya adalah antarmuka yang secara implisit dapat dikonversi keIAatauIB. Anggota ini tidak ditemukan melalui pencarian anggota pada variabel jenisC.interface IA { public int P { get { return 10; } } public void M() { Console.WriteLine("IA.M"); } } interface IB : IA { public new int P { get { return 20; } } void IA.M() { Console.WriteLine("IB.M"); } } class C : IB { } class Test { public static void Main() { C c = new C(); ((IA)c).M(); // cast needed Console.WriteLine($"IA.P = {((IA)c).P}"); // cast needed Console.WriteLine($"IB.P = {((IB)c).P}"); // cast needed } }Dalam antarmuka
IAdanIB, anggotaMdapat diakses langsung berdasarkan nama. Namun, dalam metodeMain, kami tidak dapat menulisc.M()atauc.P, karena nama-nama tersebut tidak terlihat. Untuk menemukannya, diperlukan transmisi ke jenis antarmuka yang sesuai. Deklarasi dalamMmenggunakan sintaks implementasiIBantarmuka eksplisit. Ini diperlukan untuk membuat metode tersebut mengambil alih metode diIA; pengubahoverridemungkin tidak diterapkan ke anggota fungsi. contoh akhir
19.4.2 Bidang antarmuka
Klausa ini menambah deskripsi bidang di kelas §15,5 untuk bidang yang dideklarasikan dalam antarmuka.
Bidang antarmuka dinyatakan menggunakan field_declaration(§15.5.1) dengan aturan tambahan berikut:
- Ini adalah kesalahan waktu kompilasi bagi field_declaration untuk mendeklarasikan bidang instans.
Contoh: Program berikut berisi anggota statis dari berbagai jenis:
public interface IX { public const int Constant = 100; protected static int field; static IX() { Console.WriteLine("static members initialized"); Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); field = 50; Console.WriteLine("static constructor has run"); } } public class Test: IX { public static void Main() { Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}"); } }Output yang dihasilkan adalah
static members initialized constant = 100, field = 0 static constructor has run constant = 100, field = 50contoh akhir
Lihat §19.4.8 untuk informasi mengenai alokasi dan inisialisasi bidang statis.
19.4.3 Metode antarmuka
Klausa ini menambah deskripsi metode di kelas §15.6 untuk metode yang dideklarasikan dalam antarmuka.
Metode antarmuka dinyatakan menggunakan method_declarations (§15.6)). Atribut, return_type, ref_return_type, pengidentifikasi, dan parameter_list deklarasi metode antarmuka memiliki arti yang sama dengan deklarasi metode di kelas. Metode antarmuka memiliki aturan tambahan berikut:
method_modifier tidak boleh menyertakan
override.Metode yang tubuhnya adalah titik koma (
;) adalahabstract; pengubahabstracttidak diperlukan, tetapi diizinkan.Deklarasi metode antarmuka yang memiliki badan blok atau badan ekspresi sebagai method_body adalah
virtual;virtualpengubah tidak diperlukan, tetapi diizinkan.Method_declaration tidak boleh memiliki type_parameter_constraints_clausekecuali juga memiliki type_parameter_list.
Daftar persyaratan untuk kombinasi pengubah yang valid yang dinyatakan untuk metode kelas diperluas, sebagai berikut:
- Deklarasi statis yang tidak diekstern harus memiliki badan blok atau badan ekspresi sebagai method_body.
- Deklarasi virtual yang tidak diekstern harus memiliki badan blok atau badan ekspresi sebagai method_body.
- Deklarasi privat yang tidak diekstern harus memiliki badan blok atau badan ekspresi sebagai method_body.
- Deklarasi tertutup yang tidak diekstern harus memiliki badan blok atau badan ekspresi sebagai method_body.
- Deklarasi asinkron harus memiliki badan blok atau badan ekspresi sebagai method_body.
Semua jenis parameter metode antarmuka harus aman input (§19.2.3.2), dan jenis pengembalian harus
voidaman atau output.Setiap jenis parameter output atau referensi juga harus aman output.
Catatan: Parameter output diperlukan agar aman input karena pembatasan implementasi umum. catatan akhir
Setiap batasan jenis kelas, batasan jenis antarmuka, dan batasan parameter jenis pada parameter jenis apa pun dari metode harus aman input.
Aturan ini memastikan bahwa setiap penggunaan antarmuka yang kovarian atau kontravarian tetap typeafe.
Contoh:
interface I<out T> { void M<U>() where U : T; // Error }salah bentuk karena penggunaan
Tsebagai batasan parameter jenis padaUyang tidak aman untuk input.Jika pembatasan ini tidak diberlakukan, maka akan mungkin untuk melanggar keamanan tipe dengan cara berikut:
interface I<out T> { void M<U>() where U : T; } class B {} class D : B {} class E : B {} class C : I<D> { public void M<D>() {...} } ... I<B> b = new C(); b.M<E>();Ini sebenarnya panggilan ke
C.M<E>. Tetapi panggilan itu mengharuskan bahwaEberasal dariD, sehingga keamanan tipe akan dilanggar di sini.contoh akhir
Catatan: Lihat §19.4.2 untuk contoh yang tidak hanya menunjukkan metode statis dengan implementasi, tetapi karena metode tersebut dipanggil
Maindan memiliki jenis pengembalian dan tanda tangan yang tepat, metode ini juga merupakan titik masuk. catatan akhir
Metode virtual dengan implementasi yang dideklarasikan dalam antarmuka dapat ditimpa untuk menjadi abstrak dalam antarmuka turunan. Ini dikenal sebagai reabstraksi.
Contoh:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB: IA { abstract void IA.M(); // reabstraction of M }Ini berguna dalam antarmuka turunan di mana implementasi metode tidak tepat dan implementasi yang lebih tepat harus disediakan dengan menerapkan kelas. contoh akhir
19.4.4 Properti antarmuka
Klausa ini menambah deskripsi properti di kelas §15.7 untuk properti yang dideklarasikan dalam antarmuka.
Properti antarmuka dinyatakan menggunakan property_declaration(§15.7.1) dengan aturan tambahan berikut:
property_modifier tidak boleh menyertakan
override.Implementasi anggota antarmuka eksplisit tidak boleh berisi accessor_modifier (§15.7.3).
Antarmuka turunan dapat secara eksplisit mengimplementasikan properti antarmuka abstrak yang dideklarasikan dalam antarmuka dasar.
Catatan: Karena antarmuka tidak dapat berisi bidang instans, properti antarmuka tidak dapat menjadi properti otomatis instans, karena akan memerlukan deklarasi bidang instans tersembunyi implisit. catatan akhir
Jenis properti antarmuka harus aman keluaran jika ada aksesor get, dan harus aman masukan jika ada aksesor set.
Deklarasi metode antarmuka yang memiliki badan blok atau badan ekspresi sebagai method_body adalah
virtual;virtualpengubah tidak diperlukan, tetapi diizinkan.Instans property_declaration yang tidak memiliki implementasi adalah
abstract; pengubahabstracttidak diperlukan, tetapi diizinkan. Ini tidak pernah dianggap sebagai properti yang diimplementasikan secara otomatis (§15.7.4).
19.4.5 Peristiwa antarmuka
Klausa ini menambah deskripsi peristiwa di kelas §15.8 untuk peristiwa yang dideklarasikan dalam antarmuka.
Peristiwa antarmuka dinyatakan menggunakan event_declaration(§15.8.1), dengan aturan tambahan berikut:
-
event_modifier tidak boleh menyertakan
override. - Antarmuka turunan dapat mengimplementasikan peristiwa antarmuka abstrak yang dideklarasikan dalam antarmuka dasar (§15.8.5).
- Ini adalah kesalahan waktu kompilasi untuk variable_declarators dalam instans event_declaration untuk berisi variable_initializer.
- Peristiwa instans dengan pengubah
virtualatausealedharus mendeklarasikan aksesor. Ini tidak pernah dianggap sebagai peristiwa seperti bidang yang diimplementasikan secara otomatis (§15.8.2). - Peristiwa instans dengan pengubah
abstracttidak boleh mendeklarasikan aksesor. - Jenis peristiwa antarmuka harus aman untuk input.
19.4.6 Pengindeks antarmuka
Klausa ini menambah deskripsi pengindeks di kelas §15.9 untuk pengindeks yang dideklarasikan dalam antarmuka.
Pengindeks antarmuka dinyatakan menggunakan indexer_declaration(§15,9), dengan aturan tambahan berikut:
indexer_modifier tidak boleh menyertakan
override.indexer_declaration yang memiliki isi ekspresi atau berisi aksesor dengan isi blok atau isi ekspresi adalah ;
virtualpengubah tidak diperlukan, tetapi diizinkan.virtualIndexer_declaration yang badan aksesornya adalah titik koma (
;) ;abstractabstractpengubah tidak diperlukan, tetapi diizinkan.Semua jenis parameter pengindeks antarmuka harus aman input (§19.2.3.2).
Setiap jenis parameter output atau referensi juga harus aman output.
Catatan: Parameter output diperlukan agar aman input karena pembatasan implementasi umum. catatan akhir
Jenis pengindeks antarmuka harus aman output jika ada aksesor get, dan harus aman input jika ada aksesor yang ditetapkan.
19.4.7 Operator antarmuka
Klausa ini menambah deskripsi anggota operator_declaration di kelas §15.10 untuk operator yang dideklarasikan dalam antarmuka.
Operator_declaration dalam antarmuka adalah implementasinya (§19.1).
Ini adalah kesalahan waktu kompilasi bagi antarmuka untuk mendeklarasikan operator konversi, kesetaraan, atau ketidaksetaraan.
19.4.8 Konstruktor statis antarmuka
Klausa ini menambah deskripsi konstruktor statis di kelas §15.12 untuk konstruktor statis yang dideklarasikan dalam antarmuka.
Konstruktor statis untuk antarmuka tertutup (§8.4.3) dijalankan paling banyak sekali di domain aplikasi tertentu. Eksekusi konstruktor statis dipicu oleh tindakan pertama berikut yang terjadi dalam domain aplikasi:
- Salah satu anggota statis antarmuka dirujuk.
- Sebelum metode dipanggil
Mainuntuk antarmuka yang berisiMainmetode (§7.1) di mana eksekusi dimulai. - Antarmuka tersebut menyediakan implementasi untuk anggota, dan implementasi tersebut diakses sebagai implementasi yang paling spesifik (§19.4.10) untuk anggota tersebut.
Catatan: Dalam kasus di mana tidak ada tindakan sebelumnya yang terjadi, konstruktor statis untuk antarmuka mungkin tidak dijalankan untuk program tempat instans jenis yang mengimplementasikan antarmuka dibuat dan digunakan. catatan akhir
Untuk menginisialisasi jenis antarmuka tertutup baru, pertama-tama sekumpulan bidang statis baru untuk jenis tertutup tertentu dibuat. Masing-masing bidang statis diinisialisasi ke nilai defaultnya. Selanjutnya, penginisialisasi bidang statis dijalankan untuk bidang statis tersebut. Akhirnya, konstruktor statis dijalankan.
Catatan: Lihat §19.4.2 untuk contoh penggunaan berbagai jenis anggota statis (termasuk metode Utama) yang dideklarasikan dalam antarmuka. catatan akhir
19.4.9 Jenis berlapis Antarmuka
Klausa ini menambah deskripsi jenis berlapis di kelas §15.3.9 untuk jenis berlapis yang dideklarasikan dalam antarmuka.
Ini adalah kesalahan untuk mendeklarasikan jenis kelas, jenis struct, atau jenis enum dalam cakupan parameter jenis yang dideklarasikan dengan variance_annotation (§19.2.3.1).
Contoh: Deklarasi
Cdi bawah ini adalah kesalahan.interface IOuter<out T> { class C { } // error: class declaration within scope of variant type parameter 'T' }contoh akhir
19.4.10 implementasi yang paling spesifik
Setiap kelas dan struktur harus memiliki implementasi yang paling spesifik untuk setiap anggota virtual yang dideklarasikan di semua antarmuka yang diimplementasikan oleh jenis tersebut di antara implementasi yang muncul dalam jenis atau antarmuka langsung dan tidak langsungnya. Implementasi yang paling spesifik adalah implementasi unik yang lebih spesifik daripada setiap implementasi lainnya.
Catatan: Aturan implementasi yang paling spesifik memastikan bahwa ambiguitas yang timbul dari warisan antarmuka berlian diselesaikan secara eksplisit oleh programmer pada titik di mana konflik terjadi. catatan akhir
Untuk jenis T yang merupakan struct atau kelas yang mengimplementasikan antarmuka I2 dan I3, di mana I2 dan I3 keduanya berasal secara langsung atau tidak langsung dari antarmuka I yang mendeklarasikan anggota M, implementasi M yang paling spesifik adalah:
- Jika
Tmenyatakan implementasiI.M, implementasi tersebut adalah implementasi yang paling spesifik. - Jika tidak, jika
Tadalah kelas dan kelas dasar langsung atau tidak langsung menyatakan implementasiI.M, kelas dasar yang paling turunan adalahTimplementasi yang paling spesifik. - Jika tidak, jika
I2danI3merupakan antarmuka yang diimplementasikan olehTdanI3berasal dariI2baik secara langsung atau tidak langsung,I3.Madalah implementasi yang lebih spesifik daripadaI2.M. - Jika tidak, tidak
I2.MjugaI3.Mtidak lebih spesifik dan terjadi kesalahan.
Contoh:
interface IA { void M() { Console.WriteLine("IA.M"); } } interface IB : IA { void IA.M() { Console.WriteLine("IB.M"); } } interface IC: IA { void IA.M() { Console.WriteLine("IC.M"); } } abstract class C: IB, IC { } // error: no most specific implementation for 'IA.M' abstract class D: IA, IB, IC // OK { public abstract void M(); }Aturan implementasi yang paling spesifik memastikan bahwa konflik (yaitu, ambiguitas yang timbul dari warisan berlian) diselesaikan secara eksplisit oleh programmer pada titik di mana konflik muncul. contoh akhir
19.4.11 Akses anggota antarmuka
Anggota antarmuka diakses melalui akses anggota (§12.8.7) dan akses pengindeks (§12.8.12.4) ekspresi formulir I.M dan I[A], di mana I merupakan jenis antarmuka, adalah konstanta, M bidang, metode, properti, atau peristiwa jenis antarmuka tersebut, dan A merupakan daftar argumen pengindeks.
Di kelas D, dengan kelas Bdasar langsung atau tidak langsung , di mana B secara langsung atau tidak langsung mengimplementasikan antarmuka I dan I menentukan metode M(), ekspresi base.M() hanya valid jika base.M() secara statis (§12,3) mengikat implementasi M() dalam jenis kelas.
Untuk antarmuka yang benar-benar warisan tunggal (setiap antarmuka dalam rantai warisan memiliki tepat nol atau satu antarmuka dasar langsung), efek pencarian anggota (§12,5), pemanggilan metode (§12..) 8.10.2), dan aturan akses pengindeks (§12.8.12.4) sama persis dengan untuk kelas dan struktur: Lebih banyak anggota turunan menyembunyikan anggota yang kurang diturunkan dengan nama atau tanda tangan yang sama. Namun, untuk antarmuka beberapa warisan, ambiguitas dapat terjadi ketika dua atau beberapa antarmuka dasar yang tidak terkait mendeklarasikan anggota dengan nama atau tanda tangan yang sama. Subklasul ini menunjukkan beberapa contoh, beberapa di antaranya menyebabkan ambiguitas dan lainnya yang tidak. Dalam semua kasus, pencastingan eksplisit dapat digunakan untuk menyelesaikan ambiguitas.
Contoh: Dalam kode berikut
interface IList { int Count { get; set; } } interface ICounter { int Count { get; set; } } interface IListCounter : IList, ICounter {} class C { void Test(IListCounter x) { x.Count = 1; // Error ((IList)x).Count = 1; // Ok, invokes IList.Count.set ((ICounter)x).Count = 1; // Ok, invokes ICounter.Count } }Pernyataan pertama menyebabkan kesalahan kompilasi waktu karena pencarian anggota (§12,5) untuk dalam
Countyang ambigu. Seperti yang diilustrasikan oleh contoh, ambiguitas diselesaikan dengan mentransmisikanxke jenis antarmuka dasar yang sesuai. Cast seperti itu tidak memiliki biaya run-time—hanya berarti melihat instans sebagai jenis yang lebih umum pada waktu kompilasi.contoh akhir
Contoh: Dalam kode berikut
interface IInteger { void Add(int i); } interface IDouble { void Add(double d); } interface INumber : IInteger, IDouble {} class C { void Test(INumber n) { n.Add(1); // Invokes IInteger.Add n.Add(1.0); // Only IDouble.Add is applicable ((IInteger)n).Add(1); // Only IInteger.Add is a candidate ((IDouble)n).Add(1); // Only IDouble.Add is a candidate } }pemanggilan
n.Add(1)memilihIInteger.Adddengan menerapkan aturan resolusi kelebihan beban §12.6.4. Demikian pula, pemanggilann.Add(1.0)memilihIDouble.Add. Ketika cast eksplisit dimasukkan, hanya ada satu metode yang mungkin dipilih, dan dengan demikian tidak ada ambiguitas.contoh akhir
Contoh: Dalam kode berikut
interface IBase { void F(int i); } interface ILeft : IBase { new void F(int i); } interface IRight : IBase { void G(); } interface IDerived : ILeft, IRight {} class A { void Test(IDerived d) { d.F(1); // Invokes ILeft.F ((IBase)d).F(1); // Invokes IBase.F ((ILeft)d).F(1); // Invokes ILeft.F ((IRight)d).F(1); // Invokes IBase.F } }
IBase.Fanggota disembunyikan oleh anggotaILeft.F. Pemanggiland.F(1)oleh karena itu menentukanILeft.F, meskipunIBase.Ftampaknya tidak disembunyikan di jalur akses yang mengarah melaluiIRight.Aturan intuitif untuk menyembunyikan dalam antarmuka pewarisan ganda adalah sederhana: Jika anggota disembunyikan di jalur akses mana pun, anggota tersebut disembunyikan di semua jalur akses. Karena jalur akses dari
IDerivedkeILeftkeIBasemenyembunyikanIBase.F, anggota juga tersembunyi di jalur akses dariIDerivedkeIRightkeIBase.contoh akhir
19.5 Nama anggota antarmuka yang memenuhi syarat
Anggota antarmuka terkadang disebut dengan nama anggota antarmuka yang memenuhi syarat. Nama lengkap dari anggota antarmuka terdiri dari nama antarmuka tempat anggota dideklarasikan, diikuti oleh titik, diikuti dengan nama anggota. Nama anggota yang memenuhi syarat mereferensikan antarmuka tempat anggota dideklarasikan.
Contoh: Berdasarkan deklarasi
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); }nama lengkap dari
PaintadalahIControl.Paintdan nama lengkap dari SetText adalahITextBox.SetText. Dalam contoh di atas, tidak mungkin untuk menyebutnyaPaintsebagaiITextBox.Paint.contoh akhir
Saat antarmuka adalah bagian dari namespace layanan, nama anggota antarmuka yang memenuhi syarat dapat menyertakan nama namespace layanan.
Contoh:
namespace GraphicsLib { interface IPolygon { void CalculateArea(); } }Di dalam namespace
GraphicsLib,IPolygon.CalculateAreadanGraphicsLib.IPolygon.CalculateAreakeduanya merupakan nama anggota antarmuka yang memenuhi syarat untuk metodeCalculateArea.contoh akhir
19.6 Implementasi antarmuka
19.6.1 Umum
Antarmuka dapat diimplementasikan oleh kelas dan struktur. Untuk menunjukkan bahwa kelas atau struktur secara langsung mengimplementasikan antarmuka, antarmuka disertakan dalam daftar kelas dasar kelas atau struktur.
Kelas atau struktur C yang mengimplementasikan antarmuka I harus menyediakan atau mewarisi implementasi untuk setiap anggota yang dinyatakan dalam I yang C dapat mengakses. Anggota I publik dapat didefinisikan dalam anggota publik .C Anggota non-publik yang dinyatakan dalam I yang dapat diakses C dapat didefinisikan dalam C menggunakan implementasi antarmuka eksplisit (§19.6.2).
Anggota dalam jenis turunan yang memenuhi pemetaan antarmuka (§19.6.5) tetapi tidak menerapkan anggota antarmuka dasar yang cocok memperkenalkan anggota baru. Ini terjadi ketika implementasi antarmuka eksplisit diperlukan untuk menentukan anggota antarmuka.
Contoh:
interface ICloneable { object Clone(); } interface IComparable { int CompareTo(object other); } class ListEntry : ICloneable, IComparable { public object Clone() {...} public int CompareTo(object other) {...} }contoh akhir
Kelas atau struktur yang secara langsung mengimplementasikan antarmuka juga secara implisit mengimplementasikan semua antarmuka dasar antarmuka. Ini berlaku bahkan jika kelas atau struktur tidak secara eksplisit mencantumkan semua antarmuka dasar dalam daftar kelas dasar.
Contoh:
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { public void Paint() {...} public void SetText(string text) {...} }Di sini, kelas
TextBoxmengimplementasikanIControldanITextBox.contoh akhir
Ketika kelas C secara langsung mengimplementasikan antarmuka, semua kelas yang berasal dari C juga mengimplementasikan antarmuka secara implisit.
Antarmuka dasar yang ditentukan dalam deklarasi kelas dapat dibangun jenis antarmuka (§8.4, §19.2).
Contoh: Kode berikut menggambarkan bagaimana kelas dapat mengimplementasikan jenis antarmuka yang dibangun:
class C<U, V> {} interface I1<V> {} class D : C<string, int>, I1<string> {} class E<T> : C<int, T>, I1<T> {}contoh akhir
Antarmuka dasar deklarasi kelas generik harus memenuhi aturan keunikan yang dijelaskan dalam §19.6.3.
19.6.2 Implementasi anggota antarmuka eksplisit
Untuk tujuan menerapkan antarmuka, kelas, struct, atau antarmuka dapat mendeklarasikan implementasi anggota antarmuka eksplisit. Implementasi anggota antarmuka eksplisit adalah metode, properti, peristiwa, atau deklarasi pengindeks yang mereferensikan nama anggota antarmuka yang memenuhi syarat. Kelas atau struktur yang mengimplementasikan anggota non-publik dalam antarmuka dasar harus mendeklarasikan implementasi anggota antarmuka eksplisit. Antarmuka yang mengimplementasikan anggota dalam antarmuka dasar harus mendeklarasikan implementasi anggota antarmuka eksplisit.
Anggota antarmuka turunan yang memenuhi pemetaan antarmuka (§19.6.5) menyembunyikan anggota antarmuka dasar (§7.7.2). Pengkompilasi akan mengeluarkan peringatan kecuali pengubah new ada.
Contoh:
interface IList<T> { T[] GetElements(); } interface IDictionary<K, V> { V this[K key] { get; } void Add(K key, V value); } class List<T> : IList<T>, IDictionary<int, T> { public T[] GetElements() {...} T IDictionary<int, T>.this[int index] {...} void IDictionary<int, T>.Add(int index, T value) {...} }Berikut
IDictionary<int,T>.thisdanIDictionary<int,T>.Addadalah implementasi anggota antarmuka eksplisit.contoh akhir
Contoh: Dalam beberapa kasus, nama anggota antarmuka mungkin tidak sesuai untuk kelas penerapan, dalam hal ini, anggota antarmuka dapat diimplementasikan menggunakan implementasi anggota antarmuka eksplisit. Kelas yang menerapkan abstraksi file, misalnya, kemungkinan akan mengimplementasikan fungsi anggota
Closeyang memiliki efek melepaskan sumber daya file, dan menerapkan metode antarmukaDisposedengan menggunakan implementasi anggota antarmuka eksplisit:interface IDisposable { void Dispose(); } class MyFile : IDisposable { void IDisposable.Dispose() => Close(); public void Close() { // Do what's necessary to close the file System.GC.SuppressFinalize(this); } }contoh akhir
Tidak memungkinkan untuk mengakses implementasi anggota antarmuka eksplisit melalui nama anggota antarmuka yang telah ditentukan dalam pemanggilan metode, akses properti, akses peristiwa, atau akses pengindeks. Implementasi anggota instans antarmuka eksplisit hanya dapat diakses melalui instans antarmuka, dan dalam hal ini dirujuk hanya dengan nama anggotanya. Implementasi anggota statis antarmuka eksplisit hanya dapat diakses melalui nama antarmuka.
Kesalahan terjadi pada waktu kompilasi jika implementasi anggota antarmuka eksplisit menyertakan pengubah apa pun (§15.6) selain extern atau async.
Implementasi metode antarmuka eksplisit mewarisi batasan parameter jenis apa pun dari antarmuka.
Type_parameter_constraints_clause pada implementasi metode antarmuka eksplisit hanya dapat terdiri dari class atau structprimary_constraintyang diterapkan pada type_parameteryang diketahui sesuai dengan batasan yang diwariskan untuk menjadi jenis referensi atau nilai masing-masing. Semua jenis formulir T? dalam tanda tangan implementasi metode antarmuka eksplisit, di mana T adalah parameter jenis, ditafsirkan sebagai berikut:
-
classJika batasan ditambahkan untuk parameter jenisT, makaT?adalah tipe referensi nullable; atau sebaliknya, - Jika tidak ada batasan tambahan, atau
structbatasan ditambahkan, untuk parameter jenisT, makaT?adalah tipe nilai nullable.
Contoh: Berikut ini menunjukkan cara kerja aturan saat parameter jenis terlibat:
#nullable enable interface I { void Foo<T>(T? value) where T : class; void Foo<T>(T? value) where T : struct; } class C : I { void I.Foo<T>(T? value) where T : class { } void I.Foo<T>(T? value) where T : struct { } }Tanpa batasan parameter jenis
where T : class, metode dasar dengan parameter jenis tipe referensi tidak dapat di-override. contoh akhir
Catatan: Implementasi anggota antarmuka eksplisit memiliki karakteristik aksesibilitas yang berbeda dari anggota lain. Karena implementasi anggota antarmuka eksplisit tidak pernah dapat diakses melalui nama anggota antarmuka yang memenuhi syarat saat pemanggilan metode atau saat mengakses properti, mereka bersifat privat dalam pengertian tertentu. Namun, karena mereka dapat diakses melalui antarmuka, mereka, dalam hal ini, sama terbukanya dengan antarmuka tempat mereka dideklarasikan. Implementasi anggota antarmuka eksplisit melayani dua tujuan utama:
- Karena implementasi anggota antarmuka yang eksplisit tidak dapat diakses melalui instans kelas atau struct, mereka memungkinkan agar implementasi antarmuka ini dikecualikan dari antarmuka publik kelas atau struct. Ini sangat berguna ketika kelas atau struktur mengimplementasikan antarmuka internal yang tidak menarik bagi konsumen kelas atau struktur tersebut.
- Implementasi anggota antarmuka eksplisit memungkinkan disambiguasi anggota antarmuka dengan tanda tangan yang sama. Tanpa implementasi anggota antarmuka eksplisit, tidak mungkin bagi kelas, struktur, atau antarmuka untuk memiliki implementasi anggota antarmuka yang berbeda dengan tanda tangan dan jenis pengembalian yang sama, karena tidak mungkin bagi kelas, struktur, atau antarmuka untuk memiliki implementasi apa pun di semua anggota antarmuka dengan tanda tangan yang sama tetapi dengan jenis pengembalian yang berbeda.
catatan akhir
Agar implementasi anggota antarmuka eksplisit valid, kelas, struktur, atau antarmuka harus memberi nama antarmuka di kelas dasar atau daftar antarmuka dasar yang berisi anggota yang nama anggota antarmuka yang memenuhi syarat, jenis, jumlah parameter jenis, dan jenis parameter yang sama persis dengan implementasi anggota antarmuka eksplisit. Jika anggota fungsi antarmuka memiliki array parameter, parameter yang sesuai dari implementasi anggota antarmuka eksplisit terkait diizinkan, tetapi tidak diperlukan, untuk memiliki params pengubah. Jika anggota fungsi antarmuka tidak memiliki array parameter, implementasi anggota antarmuka eksplisit terkait tidak akan memiliki array parameter.
Contoh: Dengan demikian, di kelas berikut
class Shape : ICloneable { object ICloneable.Clone() {...} int IComparable.CompareTo(object other) {...} // invalid }deklarasi hasil dalam kesalahan waktu kompilasi
IComparable.CompareTokarenaIComparabletidak tercantum dalam daftarShapekelas dasar dan bukan antarmukaICloneabledasar . Demikian juga, dalam deklarasiclass Shape : ICloneable { object ICloneable.Clone() {...} } class Ellipse : Shape { object ICloneable.Clone() {...} // invalid }deklarasi
ICloneable.ClonedalamEllipsemenghasilkan kesalahan waktu kompilasi karenaICloneabletidak secara eksplisit tercantum dalam daftar kelas dasarEllipse.contoh akhir
Nama anggota antarmuka yang memenuhi syarat dari implementasi anggota antarmuka eksplisit harus mereferensikan antarmuka tempat anggota dideklarasikan.
Contoh: Dengan demikian, dalam deklarasi
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } class TextBox : ITextBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} }implementasi anggota antarmuka eksplisit dari Paint harus ditulis sebagai
IControl.Paint, bukanITextBox.Paint.contoh akhir
19.6.3 Keunikan antarmuka yang diimplementasikan
Antarmuka yang diimplementasikan oleh deklarasi jenis generik harus tetap unik untuk semua kemungkinan jenis yang dibangun. Tanpa aturan ini, tidak mungkin untuk menentukan metode yang benar untuk memanggil jenis tertentu yang dibangun.
Contoh: Misalkan deklarasi kelas generik diizinkan untuk ditulis sebagai berikut:
interface I<T> { void F(); } class X<U ,V> : I<U>, I<V> // Error: I<U> and I<V> conflict { void I<U>.F() {...} void I<V>.F() {...} }Jika ini diizinkan, tidak mungkin untuk menentukan kode mana yang akan dijalankan dalam kasus berikut:
I<int> x = new X<int, int>(); x.F();contoh akhir
Untuk menentukan apakah daftar antarmuka deklarasi jenis generik valid, langkah-langkah berikut dilakukan:
- Biarkan
Lmenjadi daftar antarmuka yang secara langsung ditentukan di dalam kelas generik, struct, atau deklarasiC. - Tambahkan ke
Lantarmuka dasar antarmuka apa pun yang sudah ada diL. - Hapus duplikat apa pun dari
L. - Jika ada kemungkinan jenis yang mungkin dibuat dari
Cakan, setelah argumen jenis diganti menjadiL, menyebabkan dua antarmuka dalamLmenjadi identik, maka deklarasiCtidak valid. Deklarasi batasan tidak dipertimbangkan saat menentukan semua kemungkinan jenis yang dibangun.
Catatan: Dalam deklarasi
Xkelas di atas, daftarLantarmuka terdiri daril<U>danI<V>. Deklarasi tidak valid karena jenis yang dibuat denganUdanVmenjadi jenis yang sama akan menyebabkan kedua antarmuka ini menjadi jenis yang identik. catatan akhir
Dimungkinkan untuk antarmuka yang ditentukan pada tingkat pewarisan yang berbeda untuk menyatukan:
interface I<T>
{
void F();
}
class Base<U> : I<U>
{
void I<U>.F() {...}
}
class Derived<U, V> : Base<U>, I<V> // Ok
{
void I<V>.F() {...}
}
Kode ini valid meskipun Derived<U,V> mengimplementasikan baik I<U> maupun I<V>. Kode
I<int> x = new Derived<int, int>();
x.F();
memanggil metode dalam Derived, karena Derived<int,int>' secara efektif mengimplementasikan I<int> ulang (§19.6.7).
19.6.4 Implementasi metode generik
Ketika metode generik secara implisit menerapkan metode antarmuka, batasan yang diberikan untuk setiap parameter jenis metode harus setara dalam kedua deklarasi (setelah parameter jenis antarmuka diganti dengan argumen jenis yang sesuai), di mana parameter jenis metode diidentifikasi oleh posisi ordinal, kiri ke kanan.
Contoh: Dalam kode berikut:
interface I<X, Y, Z> { void F<T>(T t) where T : X; void G<T>(T t) where T : Y; void H<T>(T t) where T : Z; } class C : I<object, C, string> { public void F<T>(T t) {...} // Ok public void G<T>(T t) where T : C {...} // Ok public void H<T>(T t) where T : string {...} // Error }metode
C.F<T>secara implisit mengimplementasikanI<object,C,string>.F<T>. Dalam hal ini,C.F<T>tidak diperlukan (atau diizinkan) untuk menentukan batasanT: objectkarenaobjectmerupakan batasan implisit pada semua parameter jenis. MetodeC.G<T>secara implisit mengimplementasikanI<object,C,string>.G<T>karena batasannya sesuai dengan yang ada di antarmuka, setelah parameter tipe pada antarmuka diganti dengan argumen tipe yang sesuai. Batasan untuk metodeC.H<T>adalah kesalahan karena jenis yang disegel (stringdalam hal ini) tidak dapat digunakan sebagai batasan. Menghilangkan batasan juga akan menjadi kesalahan karena batasan implementasi metode antarmuka implisit diperlukan untuk dicocokkan. Dengan demikian, tidak mungkin untuk mengimplementasikanI<object,C,string>.H<T>secara implisit . Metode antarmuka ini hanya dapat diimplementasikan menggunakan implementasi anggota antarmuka eksplisit:class C : I<object, C, string> { ... public void H<U>(U u) where U : class {...} void I<object, C, string>.H<T>(T t) { string s = t; // Ok H<T>(t); } }Dalam hal ini, implementasi anggota antarmuka eksplisit memanggil metode publik yang memiliki batasan yang sangat lebih lemah. Penugasan dari t ke s valid karena
Tmewarisi batasanT: string, meskipun batasan ini tidak dapat diekspresikan dalam kode sumber. contoh akhir
Catatan: Ketika metode generik secara eksplisit menerapkan metode antarmuka, tidak ada batasan yang diizinkan pada metode penerapan (§15.7.1, §19.6.2). catatan akhir
Pemetaan antarmuka 19.6.5
Kelas atau struktur harus menyediakan implementasi semua anggota abstrak antarmuka yang tercantum dalam daftar kelas dasar kelas atau struktur. Proses menemukan implementasi anggota antarmuka di kelas atau struktur penerapan dikenal sebagai pemetaan antarmuka.
Pemetaan antarmuka untuk kelas atau struct C menemukan implementasi untuk setiap anggota dari setiap antarmuka yang ditentukan dalam daftar kelas dasar C. Implementasi anggota I.Mantarmuka tertentu , di mana I adalah antarmuka di mana anggota M dideklarasikan, ditentukan dengan memeriksa setiap kelas, antarmuka, atau struct S, dimulai dengan C dan mengulangi untuk setiap kelas dasar berturut-turut dan antarmuka yang diimplementasikan dari C, sampai kecocokan terletak:
- Jika
Sberisi deklarasi implementasi anggota antarmuka eksplisit yang cocokIdanM, maka anggota ini adalah implementasi dariI.M. - Jika tidak, jika
Sberisi deklarasi anggota publik non-statis yang cocokM, maka anggota ini adalah implementasi dariI.M. Jika lebih dari satu anggota cocok, tidak ditentukan anggota mana yang merupakan implementasi dariI.M. Situasi ini hanya dapat terjadi jikaSadalah jenis yang dibangun di mana kedua anggota seperti yang dinyatakan dalam jenis generik memiliki tanda tangan yang berbeda, tetapi argumen jenis membuat tanda tangan mereka identik.
Kesalahan waktu kompilasi terjadi jika implementasi tidak dapat ditemukan untuk semua anggota dari semua antarmuka yang ditentukan dalam daftar Ckelas dasar . Anggota antarmuka mencakup anggota yang diwarisi dari antarmuka dasar.
Anggota dari tipe antarmuka yang dibentuk dianggap memiliki parameter tipe apa pun yang diganti dengan argumen tipe yang sesuai seperti yang ditentukan dalam §15.3.3.
Contoh: Misalnya, mengingat deklarasi antarmuka generik:
interface I<T> { T F(int x, T[,] y); T this[int y] { get; } }antarmuka
I<string[]>yang dibangun memiliki anggota:string[] F(int x, string[,][] y); string[] this[int y] { get; }contoh akhir
Untuk tujuan pemetaan antarmuka, kelas, antarmuka, atau anggota A struct cocok dengan anggota B antarmuka saat:
-
AdanBmerupakan metode, serta nama, jenis, dan daftar parameter danABidentik. -
AdanBmerupakan properti, nama dan jenisAdanBidentik, danAmemiliki pengakses yang sama denganB(Adiizinkan untuk memiliki aksesor tambahan jika bukan implementasi anggota antarmuka eksplisit). -
AdanBmerupakan peristiwa, serta nama dan jenisAsertaBidentik. -
AdanBadalah pengindeks, jenis dan daftarAparameter danBidentik, danAmemiliki pengakses yang sama denganB(Adiizinkan untuk memiliki aksesor tambahan jika bukan implementasi anggota antarmuka eksplisit).
Implikasi penting dari algoritma pemetaan antarmuka adalah:
- Implementasi anggota antarmuka eksplisit lebih diutamakan daripada anggota lain di kelas atau struktur yang sama saat menentukan anggota kelas atau struktur yang mengimplementasikan anggota antarmuka.
- Baik non-publik maupun anggota statis tidak berpartisipasi dalam pemetaan antarmuka.
Contoh: Dalam kode berikut
interface ICloneable { object Clone(); } class C : ICloneable { object ICloneable.Clone() {...} public object Clone() {...} }anggota
ICloneable.ClonedariCmengimplementasikanClonedalamICloneablekarena implementasi anggota antarmuka yang eksplisit lebih diutamakan daripada anggota lain.contoh akhir
Jika kelas atau struct mengimplementasikan dua antarmuka atau lebih yang berisi anggota dengan nama, jenis, dan jenis parameter yang sama, dimungkinkan untuk memetakan masing-masing anggota antarmuka tersebut ke satu kelas atau anggota struct.
Contoh:
interface IControl { void Paint(); } interface IForm { void Paint(); } class Page : IControl, IForm { public void Paint() {...} }Pada konteks ini, metode
PaintdariIControldanIFormdipetakan ke metodePaintdiPage. Tentu saja juga dimungkinkan untuk memiliki implementasi anggota antarmuka eksplisit terpisah untuk dua metode tersebut.contoh akhir
Jika kelas atau struct mengimplementasikan antarmuka yang berisi anggota tersembunyi, beberapa anggota mungkin perlu diimplementasikan melalui implementasi anggota antarmuka eksplisit.
Contoh:
interface IBase { int P { get; } } interface IDerived : IBase { new int P(); }Implementasi antarmuka ini akan memerlukan setidaknya satu implementasi anggota antarmuka eksplisit, dan akan mengambil salah satu bentuk berikut
class C1 : IDerived { int IBase.P { get; } int IDerived.P() {...} } class C2 : IDerived { public int P { get; } int IDerived.P() {...} } class C3 : IDerived { int IBase.P { get; } public int P() {...} }contoh akhir
Ketika kelas mengimplementasikan beberapa antarmuka yang memiliki antarmuka dasar yang sama, hanya ada satu implementasi antarmuka dasar.
Contoh: Dalam kode berikut
interface IControl { void Paint(); } interface ITextBox : IControl { void SetText(string text); } interface IListBox : IControl { void SetItems(string[] items); } class ComboBox : IControl, ITextBox, IListBox { void IControl.Paint() {...} void ITextBox.SetText(string text) {...} void IListBox.SetItems(string[] items) {...} }tidak dimungkinkan untuk memiliki implementasi terpisah untuk yang dinamai
IControldalam daftar kelas dasar, yangIControldiwariskan olehITextBox, dan yangIControldiwariskan olehIListBox. Memang, tidak ada gagasan tentang identitas terpisah untuk antarmuka ini. Sebaliknya, implementasiITextBoxdanIListBoxberbagi implementasi yang sama dariIControl, danComboBoxhanya dianggap untuk mengimplementasikan tiga antarmuka,IControl, ,ITextBoxdanIListBox.contoh akhir
Anggota kelas dasar berpartisipasi dalam pemetaan antarmuka.
Contoh: Dalam kode berikut
interface Interface1 { void F(); } class Class1 { public void F() {} public void G() {} } class Class2 : Class1, Interface1 { public new void G() {} }metode
Fyang digunakan dalam implementasiClass1dariClass2'sInterface1.contoh akhir
19.6.6 Pewarisan implementasi antarmuka
Kelas mewarisi semua implementasi antarmuka yang disediakan oleh kelas dasarnya.
Tanpa menerapkan kembali antarmuka secara eksplisit, kelas turunan tidak dapat dengan cara apa pun mengubah pemetaan antarmuka yang diwarisinya dari kelas dasarnya.
Contoh: Dalam deklarasi
interface IControl { void Paint(); } class Control : IControl { public void Paint() {...} } class TextBox : Control { public new void Paint() {...} }
Paintmetode dalamTextBoxmenyembunyikanPaintmetode diControl, tetapi tidak mengubah pemetaanControl.PaintkeIControl.Paint, dan panggilan kePaintmelalui instans kelas dan instans antarmuka akan memiliki efek berikutControl c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes Control.Paint();contoh akhir
Namun, ketika metode antarmuka dipetakan ke metode virtual di kelas, dimungkinkan bagi kelas turunan untuk mengambil alih metode virtual dan mengubah implementasi antarmuka.
Contoh: Menulis ulang deklarasi di atas ke
interface IControl { void Paint(); } class Control : IControl { public virtual void Paint() {...} } class TextBox : Control { public override void Paint() {...} }efek berikut sekarang akan diamati
Control c = new Control(); TextBox t = new TextBox(); IControl ic = c; IControl it = t; c.Paint(); // invokes Control.Paint(); t.Paint(); // invokes TextBox.Paint(); ic.Paint(); // invokes Control.Paint(); it.Paint(); // invokes TextBox.Paint();contoh akhir
Karena implementasi anggota antarmuka eksplisit tidak dapat dinyatakan virtual, tidak mungkin untuk mengambil alih implementasi anggota antarmuka eksplisit. Namun, sangat valid untuk implementasi anggota antarmuka eksplisit untuk memanggil metode lain, dan bahwa metode lain dapat dinyatakan virtual untuk memungkinkan kelas turunan mengambil alihnya.
Contoh:
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() { PaintControl(); } protected virtual void PaintControl() {...} } class TextBox : Control { protected override void PaintControl() {...} }Di sini, kelas yang berasal dari
Controldapat mengkhasanalisasi implementasiIControl.Paintdengan mengesampingkanPaintControlmetode .contoh akhir
19.6.7 Implementasi ulang antarmuka
Kelas yang mewarisi implementasi antarmuka diizinkan untuk mengimplementasikan ulang antarmuka dengan menyertakannya dalam daftar kelas dasar.
Implementasi ulang antarmuka mengikuti aturan pemetaan antarmuka yang sama persis dengan implementasi awal antarmuka. Dengan demikian, pemetaan antarmuka yang diwariskan tidak berpengaruh apa pun pada pemetaan antarmuka yang ditetapkan untuk implementasi ulang antarmuka.
Contoh: Dalam deklarasi
interface IControl { void Paint(); } class Control : IControl { void IControl.Paint() {...} } class MyControl : Control, IControl { public void Paint() {} }fakta bahwa
ControlmemetakanIControl.PaintkeControl.IControl.Painttidak memengaruhi implementasi ulang diMyControl, yang memetakanIControl.PaintkeMyControl.Paint.contoh akhir
Deklarasi anggota publik yang diwarisi dan deklarasi anggota antarmuka eksplisit yang diwarisi ikut serta dalam proses pemetaan antarmuka untuk antarmuka yang diterapkan kembali.
Contoh:
interface IMethods { void F(); void G(); void H(); void I(); } class Base : IMethods { void IMethods.F() {} void IMethods.G() {} public void H() {} public void I() {} } class Derived : Base, IMethods { public void F() {} void IMethods.H() {} }Di sini, implementasi
IMethodsdalamDerivedmemetakan metode antarmuka keDerived.F, ,Base.IMethods.G,Derived.IMethods.HdanBase.I.contoh akhir
Ketika kelas mengimplementasikan antarmuka, secara implisit juga mengimplementasikan semua antarmuka dasar antarmuka tersebut. Demikian juga, implementasi ulang antarmuka juga secara implisit merupakan implementasi ulang semua antarmuka dasar antarmuka.
Contoh:
interface IBase { void F(); } interface IDerived : IBase { void G(); } class C : IDerived { void IBase.F() {...} void IDerived.G() {...} } class D : C, IDerived { public void F() {...} public void G() {...} }Di sini, implementasi
IDerivedulang juga mengimplementasikanIBaseulang , pemetaanIBase.FkeD.F.contoh akhir
19.6.8 Kelas dan antarmuka abstrak
Seperti kelas non-abstrak, kelas abstrak harus menyediakan implementasi semua anggota abstrak antarmuka yang tercantum dalam daftar kelas dasar kelas. Namun, kelas abstrak diizinkan untuk memetakan metode antarmuka ke metode abstrak.
Contoh:
interface IMethods { void F(); void G(); } abstract class C : IMethods { public abstract void F(); public abstract void G(); }Di sini, implementasi
IMethodsmemetakanFdanGke dalam metode-metode abstrak, yang harus ditimpa dalam kelas-kelas non-abstrak yang diturunkan dariC.contoh akhir
Implementasi anggota antarmuka eksplisit tidak dapat abstrak, tetapi implementasi anggota antarmuka eksplisit tentu saja diizinkan untuk memanggil metode abstrak.
Contoh:
interface IMethods { void F(); void G(); } abstract class C: IMethods { void IMethods.F() { FF(); } void IMethods.G() { GG(); } protected abstract void FF(); protected abstract void GG(); }Di sini, kelas non-abstrak yang berasal
Cakan diperlukan untuk mengambil alihFFdanGG, sehingga memberikan implementasi aktual dariIMethods.contoh akhir
ECMA C# draft specification