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.
21.1 Umum
Deklarasi delegasi mendefinisikan kelas yang berasal dari kelas System.Delegate. Instans delegasi merangkum daftar pemanggilan, yang merupakan daftar satu atau beberapa metode, yang masing-masing disebut sebagai entitas yang dapat dipanggil. Untuk metode instans, entitas yang dapat dipanggil terdiri dari instans dan metode pada instans tersebut. Untuk metode statis, entitas yang dapat dipanggil hanya terdiri dari metode. Memanggil instans delegasi dengan sekumpulan argumen yang sesuai menyebabkan setiap entitas delegasi yang dapat dipanggil dipanggil dengan sekumpulan argumen yang diberikan.
Catatan: Properti menarik dan berguna dari instans delegasi adalah bahwa ia tidak tahu atau peduli tentang kelas metode yang dienkapsulasinya; yang penting adalah bahwa metode tersebut kompatibel (§21.4) dengan jenis delegasi. Ini membuat delegasi sangat cocok untuk pemanggilan "anonim". catatan akhir
21.2 Deklarasi delegasi
delegate_declaration adalah type_declaration (§14,7) yang mendeklarasikan jenis delegasi baru.
delegate_declaration
: attributes? delegate_modifier* 'delegate' return_type delegate_header
| attributes? delegate_modifier* 'delegate' ref_kind ref_return_type
delegate_header
;
delegate_header
: identifier '(' parameter_list? ')' ';'
| identifier variant_type_parameter_list '(' parameter_list? ')'
type_parameter_constraints_clause* ';'
;
delegate_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
unsafe_modifier didefinisikan dalam §24.2.
Ini adalah kesalahan waktu kompilasi agar pengubah yang sama muncul beberapa kali dalam deklarasi delegasi.
Deklarasi delegasi yang menyediakan variant_type_parameter_list adalah deklarasi delegasi generik. Selain itu, setiap delegasi yang bersarang di dalam deklarasi kelas generik atau deklarasi struct generik adalah deklarasi delegasi generik, karena argumen jenis untuk jenis yang berisi harus disediakan untuk membuat jenis yang dibangun (§8.4).
Pengubah new hanya diizinkan pada delegasi yang dideklarasikan dalam jenis lain, dalam hal ini menentukan bahwa delegasi tersebut menyembunyikan anggota yang diwariskan dengan nama yang sama, seperti yang dijelaskan dalam §15.3.5.
Pengubah public, protected, internal, dan private mengontrol aksesibilitas jenis delegasi. Tergantung pada konteks di mana deklarasi delegasi terjadi, beberapa pengubah ini mungkin tidak diizinkan (§7.5.2).
Nama jenis delegasi adalah pengidentifikasi.
Seperti metode (§15.6.1), jika ref ada, delegasi mengembalikan-demi-ref; jika tidak, jika return_type adalah void, delegasi mengembalikan-tanpa-nilai; jika tidak, delegasi mengembalikan-berdasarkan-nilai.
parameter_list opsional menentukan parameter delegasi.
Return_type deklarasi delegasi returns-by-value atau returns-no-value menentukan jenis hasil, jika ada, yang dikembalikan oleh delegasi.
Ref_return_type deklarasi delegasi returns-by-ref menentukan jenis variabel yang direferensikan oleh variable_reference (§9,5) yang dikembalikan oleh delegasi.
variant_type_parameter_list opsional (§19.2.3) menentukan parameter jenis untuk delegasi itu sendiri.
Jenis pengembalian jenis delegasi harus void, atau output-safe (§19.2.3.2).
Semua jenis parameter jenis delegasi harus aman input (§19.2.3.2). Selain itu, setiap jenis parameter output atau referensi juga akan aman output.
Catatan: Parameter output diperlukan agar aman input karena pembatasan implementasi umum. catatan akhir
Selain itu, setiap batasan jenis kelas, batasan jenis antarmuka, dan batasan parameter jenis pada parameter jenis apa pun dari delegasi harus aman input.
Jenis delegasi dalam C# setara dengan nama, tidak setara secara struktural.
Contoh:
delegate int D1(int i, double d); delegate int D2(int c, double d);Jenis
D1delegasi danD2merupakan dua jenis yang berbeda, sehingga tidak dapat dipertukarkan, meskipun tanda tangannya identik.contoh akhir
Seperti deklarasi jenis generik lainnya, argumen jenis harus diberikan untuk membuat jenis delegasi yang dibangun. Jenis parameter dan jenis pengembalian jenis delegasi yang dibangun dibuat dengan mengganti, untuk setiap parameter jenis dalam deklarasi delegasi, argumen jenis yang sesuai dari jenis delegasi yang dibangun.
Satu-satunya cara untuk mendeklarasikan jenis delegasi adalah melalui delegate_declaration. Setiap jenis delegasi adalah jenis referensi yang berasal dari System.Delegate. Anggota yang diperlukan untuk setiap jenis delegasi dirinci dalam §21.3. Jenis delegasi secara sealedimplisit , sehingga tidak diizinkan untuk mendapatkan jenis apa pun dari jenis delegasi. Juga tidak diizinkan untuk menyatakan jenis kelas non-delegasi yang berasal dari System.Delegate.
System.Delegate bukan tipe delegasi; ini adalah jenis kelas tempat semua jenis delegasi diturunkan.
21.3 Mendelegasikan anggota
Setiap jenis delegasi mewarisi anggota dari Delegate kelas seperti yang dijelaskan dalam §15.3.4. Selain itu, setiap jenis delegasi harus menyediakan metode non-generik Invoke yang daftar parameternya cocok dengan parameter_list dalam deklarasi delegasi, yang jenis pengembaliannya cocok dengan return_type atau ref_return_type dalam deklarasi delegasi, dan untuk delegasi returns-by-ref yang ref_kind nya cocok dengan yang dalam deklarasi delegasi. Metode Invoke harus setidaknya dapat diakses sebagai jenis delegasi yang berisi. Memanggil Invoke metode pada jenis delegasi secara semantik setara dengan menggunakan sintaks pemanggilan delegasi (§21.6) .
Implementasi dapat menentukan anggota tambahan dalam jenis delegasi.
Kecuali untuk instans, setiap operasi yang dapat diterapkan ke instans kelas atau kelas juga dapat diterapkan ke kelas atau instans delegasi. Secara khusus, dimungkinkan untuk mengakses anggota jenis System.Delegate melalui sintaks akses anggota yang biasa.
21.4 Mendelegasikan kompatibilitas
Metode atau jenis Mdelegasi kompatibel dengan jenis D delegasi jika semua hal berikut ini benar:
-
DdanMmemiliki jumlah parameter yang sama, dan setiap parameter diDmemiliki pengubah parameter by-reference yang sama dengan parameter yang sesuai diM. - Untuk setiap parameter nilai, konversi identitas (§10.2.2
- Untuk setiap parameter referensi, jenis parameter di
Dsama dengan jenis parameter diM. - Salah satu hal berikut ini benar:
-
DdanMkeduanya mengembalikantanpa nilai . -
DdanMadalah returns-by-value (§15.6.1, §21.2), dan konversi referensi identitas atau implisit ada dari jenisMpengembalian ke jenis pengembalian .D -
DdanMkeduanya adalah returns-by-ref, konversi identitas ada antara jenisMpengembalian dan jenisDpengembalian , dan keduanya memiliki ref_kind yang sama.
-
Definisi kompatibilitas ini memungkinkan kovariansi dalam jenis pengembalian dan kontravariansi dalam jenis parameter.
Contoh:
delegate int D1(int i, double d); delegate int D2(int c, double d); delegate object D3(string s); class A { public static int M1(int a, double b) {...} } class B { public static int M1(int f, double g) {...} public static void M2(int k, double l) {...} public static int M3(int g) {...} public static void M4(int g) {...} public static object M5(string s) {...} public static int[] M6(object o) {...} }Metode
A.M1danB.M1kompatibel dengan jenisD1delegasi danD2, karena mereka memiliki jenis pengembalian dan daftar parameter yang sama. MetodeB.M2, , danB.M3tidak kompatibel dengan jenisB.M4delegasi danD1, karena mereka memiliki jenis pengembalian atau daftarD2parameter yang berbeda. MetodeB.M5danB.M6keduanya kompatibel dengan jenisD3delegasi .contoh akhir
Contoh:
delegate bool Predicate<T>(T value); class X { static bool F(int i) {...} static bool G(string s) {...} }Metode
X.Fini kompatibel dengan jenisPredicate<int>delegasi dan metodeX.Gini kompatibel dengan jenisPredicate<string>delegasi .contoh akhir
Catatan: Arti intuitif dari kompatibilitas delegasi adalah bahwa metode kompatibel dengan jenis delegasi jika setiap pemanggilan delegasi dapat diganti dengan pemanggilan metode tanpa melanggar keamanan jenis, memperlakukan parameter opsional dan array parameter sebagai parameter eksplisit. Misalnya, dalam kode berikut:
delegate void Action<T>(T arg); class Test { static void Print(object value) => Console.WriteLine(value); static void Main() { Action<string> log = Print; log("text"); } }Metode
Action<string>jenis delegasi karena setiap pemanggilanAction<string>delegasi juga akan menjadi pemanggilan metode yangJika penandatanganan metode
Print(object value, bool prependTimestamp = false)misalnya, metodeAction<string>menurut aturan subklausa ini.catatan akhir
21.5 Mendelegasikan instansiasi
Instans dari delegasi dibuat oleh delegate_creation_expression (§12.8.17.5), konversi ke tipe delegasi, kombinasi delegasi, atau penghapusan delegasi. Instans delegasi yang baru dibuat kemudian mengacu pada satu atau beberapa:
- Metode statis yang dirujuk dalam delegate_creation_expression, atau
- Objek target (yang tidak dapat
null) dan metode instans yang dirujuk dalam delegate_creation_expression, atau - Delegasi lain (§12.8.17.5).
Contoh:
delegate void D(int x); class C { public static void M1(int i) {...} public void M2(int i) {...} } class Test { static void Main() { D cd1 = new D(C.M1); // Static method C t = new C(); D cd2 = new D(t.M2); // Instance method D cd3 = new D(cd2); // Another delegate } }contoh akhir
Sekumpulan metode yang dienkapsulasi oleh instans delegasi disebut daftar pemanggilan. Ketika instans delegasi dibuat dari satu metode, instans tersebut merangkum metode tersebut, dan daftar pemanggilannya hanya berisi satu entri. Namun, ketika dua instans non-delegasinull digabungkan, daftar pemanggilannya digabungkan—dalam urutan operand kiri lalu operand kanan—untuk membentuk daftar pemanggilan baru, yang berisi dua entri atau lebih.
Saat delegasi baru dibuat dari satu delegasi, daftar pemanggilan yang dihasilkan hanya memiliki satu entri, yang merupakan delegasi sumber (§12.8.17.5).
Delegasi digabungkan menggunakan biner + (§12.12.5) dan += operator (§12.23.4). Delegasi dapat dihapus dari kombinasi delegasi, menggunakan biner - (§12.12.6) dan -= operator (§12.23.4). Delegasi dapat dibandingkan dengan kesetaraan (§12.14.9).
Contoh: Contoh berikut menunjukkan instansiasi sejumlah delegasi, dan daftar pemanggilan yang sesuai:
delegate void D(int x); class C { public static void M1(int i) {...} public static void M2(int i) {...} } class Test { static void Main() { D cd1 = new D(C.M1); // M1 - one entry in invocation list D cd2 = new D(C.M2); // M2 - one entry D cd3 = cd1 + cd2; // M1 + M2 - two entries D cd4 = cd3 + cd1; // M1 + M2 + M1 - three entries D cd5 = cd4 + cd3; // M1 + M2 + M1 + M1 + M2 - five entries D td3 = new D(cd3); // [M1 + M2] - ONE entry in invocation // list, which is itself a list of two methods. D td4 = td3 + cd1; // [M1 + M2] + M1 - two entries D cd6 = cd4 - cd2; // M1 + M1 - two entries in invocation list D td6 = td4 - cd2; // [M1 + M2] + M1 - two entries in invocation list, // but still three methods called, M2 not removed. } }Ketika
cd1dancd2dibuat, mereka masing-masing merangkum satu metode. Ketikacd3diinstansiasi, ia memiliki daftar pemanggilan dua metode, danM1,M2dalam urutan tersebut.cd4Daftar pemanggilan berisiM1, ,M2danM1, dalam urutan tersebut. Untukcd5, daftar pemanggilan berisiM1, ,M2,M1M1, danM2, dalam urutan tersebut.Saat membuat delegasi dari delegasi lain dengan delegate_creation_expression hasilnya memiliki daftar pemanggilan dengan struktur yang berbeda dari aslinya, tetapi yang mengakibatkan metode yang sama dipanggil dalam urutan yang sama. Ketika
td3dibuat daricd3daftar pemanggilannya hanya memiliki satu anggota, tetapi anggota tersebut adalah daftar metodeM1danM2dan metode tersebut dipanggil olehtd3dalam urutan yang sama seperti yang dipanggil olehcd3. Demikian pula ketikatd4dibuat daftar pemanggilannya hanya memiliki dua entri tetapi memanggil tiga metodeM1, ,M2danM1, dalam urutan itu seperticd4halnya.Struktur daftar pemanggilan memengaruhi pengurangan delegasi. Delegasikan
cd6, dibuat dengan mengurangicd2(yang memanggilM2) daricd4(yang memanggilM1, ,M2danM1) memanggilM1danM1. Namun mendelegasikantd6, dibuat dengan mengurangicd2(yang memanggilM2) daritd4(yang memanggilM1, ,M2danM1) masih memanggilM1,M2danM1, dalam urutan tersebut, sepertiM2bukan entri tunggal dalam daftar tetapi anggota daftar berlapis. Untuk contoh selengkapnya tentang menggabungkan delegasi (serta menghapus), lihat §21,6.contoh akhir
Setelah dibuat, instans delegasi selalu merujuk ke daftar pemanggilan yang sama.
Catatan: Ingat, ketika dua delegasi digabungkan, atau satu dihapus dari yang lain, hasil delegasi baru dengan daftar pemanggilannya sendiri; daftar pemanggilan delegasi digabungkan atau dihapus tetap tidak berubah. catatan akhir
21.6 Mendelegasikan pemanggilan
C# menyediakan sintaks khusus untuk memanggil delegasi. Ketika instans delegasi non-null yang daftar pemanggilannya berisi satu entri dipanggil, ia memanggil satu metode dengan argumen yang sama dengan yang diberikan dan mengembalikan nilai yang sama dengan metode yang dirujuk. (Lihat §12.8.10.4 untuk informasi terperinci tentang pemanggilan delegasi.) Jika pengecualian terjadi selama pemanggilan delegasi seperti itu, dan pengecualian itu tidak tertangkap dalam metode yang dipanggil, pencarian klausul tangkapan pengecualian berlanjut dalam metode yang memanggil delegasi, seolah-olah metode tersebut secara langsung memanggil metode yang dirujuk oleh delegasi.
Pemanggilan instans delegasi yang daftar pemanggilannya berisi beberapa entri berlanjut dengan memanggil setiap metode dalam daftar pemanggilan secara sinkron, secara berurutan. Setiap metode yang disebut diteruskan sekumpulan argumen yang sama seperti yang diberikan kepada instans delegasi. Jika pemanggilan delegasi seperti itu mencakup parameter referensi (§15.6.2.3.3), setiap pemanggilan metode akan terjadi dengan referensi ke variabel yang sama; perubahan ke variabel tersebut dengan satu metode dalam daftar pemanggilan akan terlihat oleh metode lebih jauh ke bawah daftar pemanggilan. Jika pemanggilan delegasi menyertakan parameter output atau nilai pengembalian, nilai akhir mereka akan berasal dari pemanggilan delegasi terakhir dalam daftar. Jika pengecualian terjadi selama pemrosesan pemanggilan delegasi seperti itu, dan pengecualian itu tidak tertangkap dalam metode yang dipanggil, pencarian klausul tangkapan pengecualian berlanjut dalam metode yang memanggil delegasi, dan metode apa pun di bawah daftar pemanggilan tidak dipanggil.
Mencoba memanggil instans delegasi yang nilainya null menghasilkan pengecualian jenis System.NullReferenceException.
Contoh: Contoh berikut menunjukkan cara membuat instans, menggabungkan, menghapus, dan memanggil delegasi:
delegate void D(int x); class C { public static void M1(int i) => Console.WriteLine("C.M1: " + i); public static void M2(int i) => Console.WriteLine("C.M2: " + i); public void M3(int i) => Console.WriteLine("C.M3: " + i); } class Test { static void Main() { D cd1 = new D(C.M1); cd1(-1); // call M1 D cd2 = new D(C.M2); cd2(-2); // call M2 D cd3 = cd1 + cd2; cd3(10); // call M1 then M2 cd3 += cd1; cd3(20); // call M1, M2, then M1 C c = new C(); D cd4 = new D(c.M3); cd3 += cd4; cd3(30); // call M1, M2, M1, then M3 cd3 -= cd1; // remove last M1 cd3(40); // call M1, M2, then M3 cd3 -= cd4; cd3(50); // call M1 then M2 cd3 -= cd2; cd3(60); // call M1 cd3 -= cd2; // impossible removal is benign cd3(60); // call M1 cd3 -= cd1; // invocation list is empty so cd3 is null // cd3(70); // System.NullReferenceException thrown cd3 -= cd1; // impossible removal is benign } }Seperti yang ditunjukkan dalam pernyataan
cd3 += cd1;, delegasi dapat hadir dalam daftar pemanggilan beberapa kali. Dalam hal ini, itu hanya dipanggil sekali per kemunculan. Dalam daftar pemanggilan seperti ini, ketika delegasi tersebut dihapus, kemunculan terakhir dalam daftar pemanggilan adalah yang benar-benar dihapus.Segera sebelum eksekusi pernyataan akhir,
cd3 -= cd1;, delegasicd3mengacu pada daftar pemanggilan kosong. Mencoba menghapus delegasi dari daftar kosong (atau untuk menghapus delegasi yang tidak ada dari daftar yang tidak kosong) bukanlah kesalahan.Output yang dihasilkan adalah:
C.M1: -1 C.M2: -2 C.M1: 10 C.M2: 10 C.M1: 20 C.M2: 20 C.M1: 20 C.M1: 30 C.M2: 30 C.M1: 30 C.M3: 30 C.M1: 40 C.M2: 40 C.M3: 40 C.M1: 50 C.M2: 50 C.M1: 60 C.M1: 60contoh akhir
ECMA C# draft specification