Delegate adalah tipe yang menyimpan reference, yakni rujukan ke satu metode atau lebih dengan tanda tangan (signature) tertentu, sehingga metode dapat dikirim, disimpan, dan dipanggil layaknya sebuah nilai. Tipe ini bersifat type-safe karena kompilator menolak setiap metode yang daftar parameter atau tipe kembaliannya tidak cocok. Topik ini membahas cara mendeklarasikan tipe delegate sendiri, menugaskan metode ke variabel delegate, menggabungkan beberapa metode menjadi delegate multicast, serta mengirim delegate sebagai argumen metode lain. Setelah topik ini dipahami, perilaku sebuah metode dapat disisipkan ke metode lain tanpa pewarisan maupun interface.
Mendeklarasikan tipe delegate#
Deklarasi tipe delegate menyerupai deklarasi metode tanpa badan, yaitu kata kunci delegate diikuti tipe kembalian, nama tipe, dan daftar parameter. Deklarasi tersebut menciptakan sebuah kelas yang diturunkan dari MulticastDelegate, sehingga variabel bertipe delegate adalah objek yang menunjuk ke metode. Metode yang ditunjuk wajib memiliki tanda tangan yang sama dengan deklarasi tipe delegate, yaitu jumlah parameter, tipe parameter, dan tipe kembalian yang serupa.
// Tipe delegate bernama Hitung menerima dua int dan mengembalikan int.
delegate int Hitung(int a, int b);Variabel bertipe Hitung hanya dapat diisi metode yang menerima dua int dan mengembalikan int. Penugasan yang ditulis dengan nama metode saja tanpa tanda kurung disebut grup metode (method group), dan kompilator mengonversinya menjadi instance delegate secara otomatis. Apabila tanda tangannya tidak cocok, kompilator menolak program dengan error CS0123 sebelum program dijalankan, sehingga kesalahan penugasan tidak pernah lolos sampai ke runtime.
Program utuh berikut memperlihatkan deklarasi tipe, penugasan metode, dan pemanggilan variabel delegate. Salin programnya ke berkas Program.cs, lalu jalankan dengan dotnet run --file Program.cs.
// Program dasar delegate: deklarasi, penugasan, pemanggilan, dan multicast.
// Variabel operasi mengacu pada satu metode, yaitu Tambah.
Hitung operasi = Tambah;
Console.WriteLine($"Tambah = {operasi(4, 5)}");
// += menambahkan metode Kali sehingga delegate menjadi multicast.
operasi += Kali;
Console.WriteLine($"Tambah + Kali = {operasi(4, 5)}");
// Hanya nilai kembali metode terakhir yang dipakai pemanggil multicast.
Console.WriteLine($"Nilai kembali multicast = {operasi(4, 5)}");
static int Tambah(int a, int b) => a + b;
static int Kali(int a, int b) => a * b;
// Tipe delegate menyerupai deklarasi metode tanpa badan.
delegate int Hitung(int a, int b);Tambah = 9
Tambah + Kali = 20
Nilai kembali multicast = 20Pemanggilan operasi(4, 5) menggunakan tanda kurung yang sama dengan pemanggilan metode biasa, tetapi yang dijalankan sebenarnya adalah metode yang sedang ditunjuk variabel tersebut. Baris pertama keluaran berasal dari Tambah, sedangkan baris kedua berasal dari Kali yang telah ditambahkan melalui +=. Perhatikan bahwa dua baris terakhir menghasilkan nilai yang sama, yaitu 20. Fenomena ini disebabkan sifat multicast yang dijelaskan pada bagian berikutnya.
Dalam berkas berbasis pernyataan top-level, deklarasi tipe ditulis setelah seluruh pernyataan, sebagaimana posisi delegate int Hitung pada program di atas. Pada kelas biasa, tipe delegate boleh dideklarasikan sebagai anggota kelas atau di tingkat namespace.
Delegate multicast#
Satu variabel delegate dapat menampung lebih dari satu metode sekaligus, dan bentuk semacam ini disebut delegate multicast. Operator += menambahkan metode ke daftar pemanggilan (invocation list), sedangkan operator -= melepas satu kemunculan yang cocok dari daftar tersebut. Ketika delegate multicast dipanggil, seluruh metode pada daftarnya dijalankan satu per satu sesuai urutan pendaftarannya.
Delegate bertipe void cocok untuk multicast karena tidak ada nilai kembali yang berisiko tertelan.
// Delegate void dipakai untuk multicast karena tidak ada nilai kembali yang hilang.
Notifikasi terima = ViaTampilan;
terima += ViaCatatan;
// Pemanggilan sekali menjalankan kedua metode sesuai urutan pendaftarannya.
terima("Suhu naik");
static void ViaTampilan(string pesan) => Console.WriteLine($"Tampilan: {pesan}");
static void ViaCatatan(string pesan) => Console.WriteLine($"Catatan: {pesan}");
delegate void Notifikasi(string pesan);Tampilan: Suhu naik
Catatan: Suhu naikUntuk delegate yang memiliki nilai kembali, hanya nilai kembali metode terakhir pada daftar pemanggilan yang diterima pemanggil, sedangkan hasil metode sebelumnya dibuang. Itulah sebabnya dua baris terakhir pada Program.cs sama-sama bernilai 20, yaitu hasil Kali yang menjadi metode terakhir. Bila keluaran dari setiap metode diperlukan, delegate yang digunakan sebaiknya bertipe void dan setiap metode menuliskan hasilnya sendiri.
Operator -= melepas satu kemunculan yang cocok dari akhir daftar pemanggilan, dan apabila seluruh metode sudah dilepas, variabel delegate kembali bernilai null. Variabel delegate yang masih null tidak dapat dipanggil, karena pemanggilan melempar exception NullReferenceException.
// Delegate yang belum menunjuk metode mana pun bernilai null.
Hitung? operasi = null;
try
{
// Tanda ! menyatakan pemeriksaan null sengaja diserahkan ke runtime.
Console.WriteLine(operasi!(1, 2));
}
catch (NullReferenceException)
{
Console.WriteLine("Delegate bernilai null tidak bisa dipanggil.");
}
delegate int Hitung(int a, int b);Delegate bernilai null tidak bisa dipanggil.Delegate sebagai argumen#
Delegate memungkinkan sebuah metode menerima perilaku dari pemanggilnya, bukan hanya data. Metode penerima memanggil delegate tersebut pada titik yang tepat, sehingga algoritmanya tetap satu, tetapi langkah detailnya dapat diganti-ganti. Pola semacam ini mendasari pemanggilan balik (callback), kueri LINQ, dan event.
Tipe Action dan Func yang tersedia di .NET adalah tipe delegate generik yang siap pakai, sehingga tipe delegate baru tidak perlu dideklarasikan untuk setiap bentuk tanda tangan. Action menampung metode tanpa nilai kembali, sedangkan Func menampung metode dengan nilai kembali, dan tipe parameternya dituliskan sebagai argumen generik.
// Delegate dikirim sebagai argumen sehingga perilaku metode dapat disisipkan.
Func<int, int> gandakan = x => x * 2;
Console.WriteLine(Terapkan(3, gandakan));
Console.WriteLine(Terapkan(5, Kuadrat));
// Metode yang diterima delegate wajib cocok dengan tanda tangannya.
static int Kuadrat(int n) => n * n;
// Metode ini menerima delegate bertipe Func dan memanggilnya sekali.
static int Terapkan(int nilai, Func<int, int> operasi) => operasi(nilai);6
25Metode Terapkan tidak mengetahui isi perhitungan yang diterimanya, karena tugasnya hanya memanggil delegate pada masukan yang diberikan. Pemanggil pertama menyisipkan penggandaan melalui lambda, sedangkan pemanggil kedua menyisipkan pengkuadratan melalui grup metode Kuadrat. Kedua pemanggilan tersebut menggunakan satu algoritma yang sama, tetapi menghasilkan perilaku yang berbeda.
Kesalahan umum#
Tiga kesalahan berikut paling sering muncul ketika delegate pertama kali digunakan.
- Tanda tangan metode tidak cocok dengan tipe delegate. Penugasan ditolak kompilator dengan error CS0123, sebagaimana contoh di bawah. Perbaikannya adalah menyesuaikan parameter dan tipe kembalian metode, atau mendeklarasikan tipe delegate yang sesuai dengan metode yang tersedia.
- Delegate yang bernilai
nulltetap dipanggil. Pemanggilan melemparNullReferenceException, sehingga delegate perlu diperiksa terlebih dahulu atau dipanggil dengan?.Invoke()bila keberadaan metodenya belum pasti. - Mengharapkan nilai kembali dari seluruh anggota multicast. Hanya nilai kembali metode terakhir yang diterima pemanggil, sehingga pengumpulan hasil sebaiknya dilakukan di dalam masing-masing metode bertipe
void.
// tidak-dijalankan
// Penugasan berikut ditolak kompilator dengan error CS0123, karena
// tanda tangan Tambah tidak cocok dengan tipe delegate Hitung.
Hitung operasi = Tambah;
delegate int Hitung(int a, int b);
static int Tambah(double a, double b) => (int)(a + b);Ringkasan#
- Delegate adalah tipe yang menyimpan reference ke metode dengan tanda tangan tertentu, dan ketidakcocokan tanda tangan ditolak kompilator sebagai error CS0123.
- Operator
+=dan-=menyusun daftar pemanggilan, sedangkan pemanggilan multicast hanya memakai nilai kembali dari metode terakhir. - Delegate dapat dikirim sebagai argumen, dan tipe generik
ActionsertaFuncadalah delegate bawaan yang menghilangkan kebutuhan deklarasi tipe baru. - Delegate yang belum menunjuk metode mana pun bernilai
nulldan memanggilnya melemparNullReferenceException.
Referensi#
- Introduction to delegates and events — Microsoft Learn — Ringkasan konsep delegate, sifat multicast, dan hubungannya dengan event.
- Work with delegate types in C# — Microsoft Learn — Panduan penggunaan tipe delegate, mulai dari penugasan metode hingga pengiriman sebagai argumen.
- How to combine delegates (Multicast Delegates) — Microsoft Learn
— Cara menyusun dan melepas anggota daftar pemanggilan dengan
+=dan-=.