13. Delegate, Event, dan
Lambda
Bab ini akan
mendiskusikan tiga fitur inovatif C#: delegate, event, dan lambda. Delegate
memberikan cara untuk mengenkapsulasi sebuah metode. Event merupakan sebuah
pemberitahuan bahwa suatu aksi telah terjadi. Delegate dan event berelasi
karena sebuah event dibangun berdasarkan delegate. Ekspresi lambda merupakan
fitur yang relatif baru di dalam C# yang menawarkan cara dalam mendefinisikan
unit kode yang dapat dieksekusi. Ekspresi lambda sering dipakai ketika bekerja
dengan delegate dan event.
Delegate
Dalam bahasa
sederhana, delegate merupakan sebuah objek yang dapat menunjuk ke suatu metode.
Oleh karena itu, ketika Anda menciptakan delegate, Anda sedang menciptakan
sebuah objek yang menampung referensi yang menunjuk ke suatu metode. Selain
itu, metode dapat dipanggil melalui referensi tersebut. Dengan kata lain,
delagate dapat memanggil metode yang ia tunjuk. Ini merupakan konsep yang
sangat berguna.
Adalah penting
untuk memahami bahwa delegate yang sama dapat dipakai untuk memanggil berbagai
metode selama program berjalan, hanya dengan mengubah metode yang ditunjuk.
Jadi, metode yang akan dipanggil oleh delegate tidak ditentukan pada saat
kompilasi, tetapi pada saat program sedang berjalan. Ini merupakan keuntungan
utama dari sebuah delegate.
Tipe delegate
dideklarasikan menggunakan katakunci delegate. Bentuk umum sebuah deklarasi
delegate ditunjukkan di sini:
delegate tipe-nilaibalik nama(daftar-parameter);
Di sini, tipe-nilaibalik adalah tipe
dari nilai yang dijadikan nilai balik oleh metode yang dipanggil oleh delegate.
Nama delegate dispesifikasi oleh nama. Parameter yang dibutuhkan oleh metode yang dipanggil
melalui delegate dispesifikasi di dalam daftar-parameter. Begitu
diciptakan, sebuah instans delegate dapat menunjuk ke dan memanggil metode yang
mempunyai nilai balik dan daftar parameter sesuai dengan yang dispesifikasi
oleh deklarasi delegate.
Poin kunci untuk
dipahami adalah bahwa delegate dapat dipakai untuk memanggil sembarang metode
yang cocok dengan sidik dan tipe nilai baliknya. Selain itu, metode bisa berupa
metode instans yang diasosiasikan dengan sebuah objek atau metode static yang diasosiasikan dengan sebuah
kelas. Yang penting adalah bahwa tipe nilai balik dan sidik metode harus sesuai
dengan delegate. Untuk melihat bagaimana delegate bekerja, akan disajikan
sebuah contoh sederhana berikut:
// Sebuah contoh delegate sederhana.
using System;
// Mendeklarasikan sebuah tipe delegate.
delegate string StrMod(string
str);
class UjiDelegate {
// Mengganti spasi
dengan tanda-hubung.
static string GantiSpasi(string s) {
Console.WriteLine("Mengganti spasi dengan tanda-hubung.");
return s.Replace(' ', '-');
}
// Menghapus spasi.
static string HapusSpasi(string
s) {
string temp = "";
int i;
Console.WriteLine("Menghaspus spasi.");
for(i=0; i < s.Length; i++)
if(s[i] != ' ') temp += s[i];
return temp;
}
// Membalikkan sebuah
string.
static string Balik(string
s) {
string temp = "";
int i, j;
Console.WriteLine("Membalikkan string.");
for(j=0, i=s.Length-1; i >= 0; i--,
j++)
temp += s[i];
return temp;
}
static void Main() {
// Menciptakan
sebuah delegate.
StrMod strOp = new StrMod(GantiSpasi);
string str;
// Memanggil
beberapa metode melalui delegate.
str = strOp("Ini adalah sebuah test.");
Console.WriteLine("String yang dihasilkan: " + str);
Console.WriteLine();
strOp = new StrMod(HapusSpasi);
str = strOp("Ini adalah sebuah test.");
Console.WriteLine("String yang dihasilkan: " + str);
Console.WriteLine();
strOp = new StrMod(Balik);
str = strOp("Ini adalah sebuah test.");
Console.WriteLine("String yang dihasilkan: " + str);
}
}
Keluaran program
yang dihasilkan adalah:
Mengganti spasi dengan tanda-hubung.
String yang dihasilkan: Ini-adalah-sebuah-test.
Menghaspus spasi.
String yang dihasilkan: Iniadalahsebuahtest.
Membalikkan string.
String yang dihasilkan: .tset haubes halada inI
Akan
didiskusikan program ini lebih detil. Program mendeklarasikan sebuah tipe
delegate yang dinamakan StrMod,
ditunjukkan di sini:
delegate string StrMod(string str);
Perhatikan bahwa
StrMod mengambil satu parameter string dan menghasilkan nilai balik
berupa sebuah string.
Selanjutnya, di
dalam UjiDelegate, tiga metode static dideklarasikan, masing-masing
dengan satu parameter bertipe string
dan menghasilkan nilai balik bertipe string.
Jadi, ketiganya cocok dengan delegate StrMod.
Ketiga metode tersebut melakukan tugas pemodifikasian string. Perhatikan bahwa GantiSpasi() menggunakan salah satu
dari metode string, Replace(), untuk
mengganti spasi dengan tanda-hubung.
Di dalam Main(), sebuah referensi StrMod(), strOp, diciptakan dan ditugasi sebuah referensi yang menunjuk ke GantiSpasi(). Perhatikan baris ini:
StrMod strOp = new StrMod(GantiSpasi);
Perhatikan
bagaiman metode GantiSpasi()
dilewatkan sebagai sebuah parameter. Hanya namanya yang dipakai; tidak ada
parameter yang dispesifikasi. Hal ini dapat digeneralisir. Ketika
menginstansiasi sebuah delegate, Anda hanya perlu menspesifikasi nama metode
yang akan ditunjuk oleh delegate tersebut. Tentu saja, sidik metode harus cocok
dengan yang ada pada deklarasi delegate. Jika tidak, error kompilasi akan
dibangkitkan sistem.
Berikutnya, GantiSpasi() dipanggil melalui instans
delegate, strOp, seperti ditunjukkan
di sini:
str = strOp("Ini adalah sebuah test.");
Karena strOp menujuk ke GantiSpasi(), maka GantiSpasi()
yang dipanggil.
Selanjutnya, strOp ditugasi sebuah referensi yang
menunjuk ke HapusSpasi(), dan
kemudian strOp dipanggil kembali.
Kali ini, HapusSpasi() yang
dipanggil.
Terakhir, strOp ditugasi sebuah referensi ke Balik() dan strOp dipanggil. Ini menyebabkan pemanggilan Balik().
Konversi
Grup Metode
Sejak versi 2.0,
C# mencantumkan sebuah opsi yang secara signifikan menyederhanakan sintaks
dalam menugaskan sebuah metode kepada delegate. Fitur ini dikenal dengan
konversi grup metode, yang menugaskan nama sebuah metode kepada delegate, tanpa
menggunakan new atau tanpa secara
eksplisit memanggil konstruktor delegate.
Sebagai contoh,
berikut metode Main() pada program
sebelumnya ditulis-ulang agar menggunakan konversi grup metode:
// Sebuah contoh delegate sederhana.
using System;
// Mendeklarasikan sebuah tipe delegate.
delegate string StrMod(string
str);
class UjiDelegate {
// Mengganti spasi
dengan tanda-hubung.
static string GantiSpasi(string s) {
Console.WriteLine("Mengganti spasi dengan tanda-hubung.");
return s.Replace(' ', '-');
}
// Menghapus spasi.
static string HapusSpasi(string s) {
string temp = "";
int i;
Console.WriteLine("Menghaspus spasi.");
for(i=0; i < s.Length; i++)
if(s[i] != ' ')
temp += s[i];
return temp;
}
// Membalikkan sebuah
string.
static string Balik(string
s) {
string temp =
"";
int i, j;
Console.WriteLine("Membalikkan string.");
for(j=0, i=s.Length-1; i >= 0; i--,
j++)
temp += s[i];
return temp;
}
static void Main() {
// Mengkonstruksi
sebuah delegate menggunakan konversi grup metode.
StrMod strOp =
GantiSpasi; // menggunakan konversi grup metode
string str;
// Memanggil
metode melalui delegate.
str =
strOp("Ini adalah sebuah test.");
Console.WriteLine("String yang dihasilkan: " + str);
Console.WriteLine();
strOp =
GantiSpasi; // menggunakan konversi grup metode
str =
strOp("Ini adalah sebuah test.");
Console.WriteLine("String yang dihasilkan: " + str);
Console.WriteLine();
strOp = Balik; //
menggunakan konversi grup metode
str =
strOp("Ini adalah sebuah test.");
Console.WriteLine("String yang dihasilkan: " + str);
}
}
Perhatikan
khusus pada cara strOp diciptakan
dan ditugasi metode GantiSpasi()
pada baris ini:
StrMod strOp =
GantiSpasi; // menggunakan
konversi grup metode
Nama metode
ditugaskan secara langsung kepada strOp.
C# secara otomatis menyediakan sebuah konversi dari metode menjadi tipe
delegate. Sintaks ini dapat digeneralisir untuk sembarang situasi dimana di
dalamnya sebuah metode ditugaskan kepada (dikonversi menjadi) suatu tipe
delegate.
Karena sintaks
konversi grup metode lebih sederhana dari pendekatan semula, sintaks tersebut
akan digunakan pada buku ini.
Menggunakan
Metode Instans Sebagai Delegate
Meskipun pada
contoh terdahulu digunakan metode static,
delegate dapat juga menunjuk ke metode instans. Hal ini harus dilakukan melalui
sebuah referensi objek. Sebagai contoh, berikut adalah versi yang ditulis-ulang
dari contoh sebelumnya, yang mengenkapsulasi beberapa operasi string di dalam
kelas StringOps. Perhatikan bahwa
sintaks konversi grup metode dapat diterapkan pada situasi ini.
// Delegate dapat menunjuk ke metode instans juga.
using System;
// Mendeklarasikan sebuah tipe delegate.
delegate string StrMod(string
str);
class StringOps {
// Mengganti spasi
dengan tanda-hubung.
public string GantiSpasi(string
s) {
Console.WriteLine("Mengganti spasi dengan tanda-hubung.");
return s.Replace(' ', '-');
}
// Menghapus spasi.
public string HapusSpasi(string
s) {
string temp = "";
int i;
Console.WriteLine("Menghaspus spasi.");
for(i=0; i < s.Length; i++)
if(s[i] != ' ') temp += s[i];
return temp;
}
// Membalikkan sebuah
string.
public string Balik(string
s) {
string temp = "";
int i, j;
Console.WriteLine("Membalikkan string.");
for(j=0, i=s.Length-1; i >= 0; i--,
j++)
temp += s[i];
return temp;
}
}
class UjiDelegate {
static void Main() {
StringOps so = new StringOps(); // menciptakan instans
dari StringOps
//
Menginisialisasi delegate.
StrMod strOp =
so.GantiSpasi;
string str;
// Memanggil
metode melalui delegate.
str =
strOp("Ini adalah sebuah test.");
Console.WriteLine("String yang dihasilkan: " + str);
Console.WriteLine();
strOp =
so.GantiSpasi; // menggunakan konversi grup metode
str =
strOp("Ini adalah sebuah test.");
Console.WriteLine("String yang dihasilkan: " + str);
Console.WriteLine();
strOp = so.Balik;
// menggunakan konversi grup metode
str =
strOp("Ini adalah sebuah test.");
Console.WriteLine("String yang dihasilkan: " + str);
}
}
Multicasting
Salah satu fitur
paling menarik dari sebuah delegate adalah multicasting. Dalam bahasa
sederhana, multicasting merupakan kemampuan untuk menciptakan daftar atau
rantai pemanggilan metode yang akan secara otomatis dipanggil ketika sebuah
delegate dipanggil. Rantai semacam itu sangat mudah diciptakan. Anda hanya
perlu menginstansiasi sebuah delegate dan kemudian menggunakan operator + atau += untuk menambahkan metode ke dalam rantai. Untuk menghapus sebuah
metode dari rantai, Anda bisa menggunakan operator – atau -=. Jika delegate
menghasilkan nilai balik, maka nilai balik yang dikembalikan oleh metode
terakhir di dalam rantai menjadi nilai balik dari keseluruhan pemanggilan
delegate. Jadi, delegate yang memanfaatkan multicasting biasanya mempunyai
nilai balik bertipe void.
Berikut adalah
sebuah contoh multicasting. Perhatikan bahwa program sebelumnya
dikerjakan-ulang dengan mengubah tipe nilai balik pada metode manipulasi string
menjadi void dan menggunakan sebuah
parameter ref untuk menghasilkan
nilai balik berupa string yang telah diubah.
// Demonstrasi multicasting.
using System;
// Mendeklarasikan sebuah tipe delegate.
delegate void StrMod(ref string
str);
class DemoMulticast {
// Mengganti spasi
dengan tanda-hubung.
static
void GantiSpasi(ref string s) {
Console.WriteLine("Mengganti spasi dengan tanda-hubung.");
s = s.Replace(' ',
'-');
}
// Menghapus spasi.
static void HapusSpasi(ref
string s) {
string temp = "";
int i;
Console.WriteLine("Menghaspus spasi.");
for(i=0; i < s.Length; i++)
if(s[i] != ' ') temp += s[i];
s = temp;
}
// Membalikkan sebuah
string.
static void Balik(ref string
s) {
string temp = "";
int i, j;
Console.WriteLine("Membalikkan string.");
for(j=0, i=s.Length-1; i >= 0; i--,
j++)
temp += s[i];
s = temp;
}
static void Main() {
// mengkonstruksi
beberapa delegate
StrMod strOp;
StrMod gantiSp =
GantiSpasi;
StrMod hapusSp =
HapusSpasi;
StrMod balikStr =
Balik;
string str =
"Ini adalah sebuah test";
// Membuat
multicast.
strOp = gantiSp;
strOp +=
balikStr;
// Memanggil
multicast.
strOp(ref str);
Console.WriteLine("String yang dihasilkan: " + str);
Console.WriteLine();
// Menghapus
ganti dan menambahkan hapus.
strOp -=
GantiSpasi;
strOp +=
HapusSpasi;
str = "Ini adalah sebuah test.";
// Memanggil
multicast.
strOp(ref str);
Console.WriteLine("String yang dihasilkan: " + str);
Console.WriteLine();
}
}
Program
menghasilkan keluaran berikut:
Mengganti spasi dengan tanda-hubung.
Membalikkan string.
String yang dihasilkan: tset-haubes-halada-inI
Membalikkan string.
Menghaspus spasi.
String yang dihasilkan: .tsethaubeshaladainI
Di dalam Main(), empat instans delegate
diciptakan. Satu, strOp, bernilai
null. Ketiga lain menunjuk ke metode modifikasi string tertentu. Selanjutnya,
multicast diciptakan untuk memanggil GantiSpasi()
dan Balik(). Ini dilakukan melalui
dua baris kode:
strOp = gantiSp;
strOp += balikStr;
Pertama, strOp ditugasi gantiSp. Selanjutnya, menggunakan operator +=, balikStr
ditambahkan. Ketika strOp dipanggil,
kedua metode dipanggil, menggantikan spasi dengan tanda-hubung dan membalikkan
string, seperti yang ditunjukkan pada keluaran program.
Selanjutnya, gantiSp dihapus dari rantai,
menggunakan baris ini:
strOp -= gantiSp;
dan hapusSp
ditambahkan menggunakan baris ini:
strOp += hapusSp;
Kemudian, strOp kembali dipanggil. Kali ini,
spasi dihapus dan string dibalikkan. Rantai delegate merupakan mekanisme yang
tangguh karena membolehkan Anda untuk mendefinisikan sehimpunan metode yang
dieksekusi dalam satu unit.
Kovariansi
Dan Kontravariansi
Ada dua fitur
tambahan bagi delegate: kovariansi dan kontravariansi. Biasanya, metode yang
Anda lewatkan kepada delegate harus mempunyai tipe nilai balik dan sidik yang
sama dengan delegate. Namun, kovariansi dan kontravariansi mengendorkan sedikit
aturan ini. Kovariansi memampukan sebuah metode untuk ditugaskan kepada sebuah
delegate manakala tipe nilai balik metode adalah sebuah kelas yang diderivasi
dari kelas yang dispesifikasi oleh tipe nilai balik delegate. Kontravariansi
memampukan sebuah metode untuk ditugaskan kepada delegate ketika tipe parameter
metode merupakan kelas basis bagi kelas yang dispesifikasi oleh deklarasi
delegate.
Berikut
disajikan sebuah contoh yang mengilustrasikan kovariansi dan kontravariansi.
// Demonstrasi kovariansi dan kontravariansi.
using System;
class X {
public int Nil;
}
//Y diwarisi dari X.
class Y : X { }
// Delegate ini menghasilkan tipe X dan mengambil argumen
tipe Y.
delegate X UbahSaja(Y obj);
class KoDanKontraVariansi {
// Metode ini
menghasilkan tipe X dan mempunyai tipe parameter X.
static X InkrA(X obj)
{
X temp = new X();
temp.Nil =
obj.Nil + 1;
return temp;
}
// Metode ini
menghasilkan tipe Y dan mempunyai tipe parameter Y.
static Y InkrB(Y obj)
{
Y temp = new Y();
temp.Nil =
obj.Nil + 1;
return temp;
}
static void Main()
{
Y Yob = new Y();
// Pada kasus
ini, parameter untuk InkrA
// adalah tipe
X dan parameter untuk UbahSaja adalah tipe Y.
// Karena
kontravariansi, baris berikut OK
// untuk
dilakukan.
UbahSaja ubah =
InkrA;
X Xob =
ubah(Yob);
Console.WriteLine("Xob: " + Xob.Nil);
// Pada kasus
berikutnya, tipe nilai balik dari
// InkrB adalah
Y dan tipe nilai balik dari
// UbahSaja
adalah X. Karena kovariansi,
// baris
berikut OK dilakukan.
ubah = InkrB;
Yob =
(Y)ubah(Yob);
Console.WriteLine("Yob: " + Yob.Nil);
}
}
Keluaran program
yang dihasilkan adalah:
Xob: 1
Yob: 1
Di dalam
program, perhatikan bahwa kelas Y
diderivasi dari kelas X.
Selanjutnya, perhatikan bahwa delegate UbahSaja
dideklarasikan seperti ini:
delegate X UbahSaja(Y obj);
UbahSaja menghasilkan nilai balik tipe X dan mempunyai parameter bertipe Y. Berikutnya, perhatikan bahwa metode InkrA() dan InkrB()
dideklarasikan seperti ini:
static X InkrA(X
obj)
static Y InkrB(Y obj)
Metode InkrA() mempunyai parameter bertipe X dan menghasilkan nilai balik bertipe X. Metode InkrB() mempunyai parameter bertipe Y dan menghasilkan nilai balik bertipe Y. Karena kovariansi dan kontravariansi, masing-masing metode
tersebut dapat dilewatkan kepada UbahSaja,
seperti diilustrasikan pada program. Oleh karena itu, baris ini
UbahSaja ubah = InkrA;
menggunakan
kontravariansi untuk memampukan InkrA()
untuk dilewatkan kepada delegate karena InkrA()
mempunyai parameter bertipe X,
padahal delegate mempunyai parameter bertipe Y. Hal ini diijinkan karena, dengan kontravariansi, jika tipe
parameter dari metode yang dilewatkan kepada delegate adalah kelas basis bagi
tipe parameter yang digunakan oleh delegate, maka metode dan delegate menjadi
kompatibel.
Baris berikutnya
juga legal, tetapi kali ini karena kovariansi:
ubah = InkrB;
Pada kasus ini,
tipe nilai balik dari InkrB() adalah
Y, padahal tipe nilai balik dari UbahSaja adalah X. Namun, karena tipe nilai balik dari metode adalah kelas yang
diderivasi dari tipe nilai balik delegate, maka keduanya menjadi kompatibel.
Metode
Anonim
Sebuah metode
anonim adalah suatu cara untuk menciptakan blok kode tak-bernama yang
diasosiasikan dengan instans delegate tertentu. Metode anonim diciptakan
menggunakan katakunci delegate
dengan blok kode. Untuk melihat bagaimana ini dilakukan, akan disajikan sebuah
contoh sederhana. Program berikut menggunakan sebuah metode anonim yang menampilkan
angka 0 sampai 5.
// Demonstrasi sebuah metode
anonim.
using System;
// Deklarasi sebuah tipe
delegate.
delegate
void
Hitung();
class DemoMetAnonim
{
static
void Main()
{
// Di sini, kode untuk penghitungan
dilewatkan
// sebagai sebuah metode anonim.
Hitung hitung = delegate
{
// Ini adalah blok kode yang
dilewatkan kepada delegate.
for (int i = 0; i <=
5; i++)
Console.WriteLine(i);
}; // perhatikan titik-koma
hitung();
}
}
Program ini
pertama-tama mendeklarasikan sebuah tipe delegate, Hitung, yang tidak memiliki parameter dan menghasilkan void. Di dalam Main(), sebuah instans Hitung,
dinamakan hitung, diciptakan. Kepada
hitung dilewakan blok kode yang
ditempatkan setelah katakunci delegate.
Blok kode ini merupakan metode anonim yang akan dieksekusi ketika hitung dipanggil. Perhatikan bahwa blok kode tersebut diakhiri
dengan sebuah titik-koma, yang menghentikan deklarasi statemen. Keluaran
program ditampilkan di sini:
0
1
2
3
4
5
Melewatkan
Argumen Kepada Metode Anonim
Anda
dimungkinkan untuk melewatkan satu atau lebih argumen kepada metode anonim.
Untuk melakukannya, tempatkan daftar parameter (yang diapit sepasang kurung)
setelah katakunci delegate.
Kemudian, lewatkan argumen tersebut kepada instans delegate ketika ia dipanggil.
Sebagai contoh, berikut merupakan program sebelumnya yang ditulis-ulang
sehingga nilai akhir nilai untuk hitung dilewatkan:
// Demonstrasi sebuah metode
anonim yang mengambil sebuah argumen.
using System;
// Perhatikan sekarang
delegate mempunyai parameter.
delegate
void
Hitung(int akhir);
class DemoMetAnonim2
{
static
void Main()
{
// Di sini, kode untuk penghitungan
dilewatkan
// sebagai sebuah metode anonim.
Hitung hitung = delegate (int akhir)
{
// Ini adalah blok kode yang
dilewatkan kepada delegate.
for (int i = 0; i <=
akhir; i++)
Console.WriteLine(i);
}; // perhatikan titik-koma
hitung(3);
Console.WriteLine();
hitung(5);
}
}
Pada versi ini, Hitung sekarang mengambil sebuah
argumen integer. Perhatikan bahwa daftar parameter dispesifikasi setelah
katakunci delegate ketika metode
anonim diciptakan. Kode di dalam metode anonim memiliki akses terhadap
parameter akhir, sama seperti bila
digunakan metode yang memiliki nama. Keluaran dari program ini ditampilkan
berikut:
0
1
2
3
0
1
2
3
4
5
Nilai
Balik Dari Metode Anonim
Sebuah metode
anonim dapat mempunyai nilai balik. Nilai tersebut dihasilkan menggunakan
statemen return, yang bekerja
seperti pada metode bernama. Seperti yang Anda duga, tipe nilai balik harus
kompatibel dengan tipe nilai balik yang dispesifikasi oleh delegate. Sebagai
contoh, berikut adalah kode yang menghitung penjumlahan atas hitung dan memberikan nilai balik:
// Demonstrasi sebuah metode
anonim yang menghasilkan nilai balik.
using System;
// Delegate ini menghasilkan
nilai balik.
delegate
int
Hitung(int akhir);
class DemoMetAnonim3
{
static
void Main()
{
int
hasil;
// Di sini, nilai akhir untuk hitung
// dilewatkan kepada metode anonim.
// Penjumlahan atas hitung dijadikan
nilai balik.
Hitung hitung = delegate (int akhir)
{
int jum = 0;
for (int i = 0; i <=
akhir; i++)
{
Console.WriteLine(i);
jum += i;
}
return jum; // nilai balik dari metode anonim
};
hasil = hitung(3);
Console.WriteLine("Penjumlahan atas 3 adalah " +
hasil);
Console.WriteLine();
hasil = hitung(5);
Console.WriteLine("Penjumlahan atas 5 adalah " +
hasil);
}
}
Pada versi ini,
nilai dari jum dijadikan nilai balik
oleh blok kode yang diasosiasikan dengan instans delegate hitung. Perhatikan bahwa statemen return dipakai di dalam metode
anonim, sama seperti ketika digunakan di dalam metode bernama. Keluaran program
ditampilkan di sini:
0
1
2
3
Penjumlahan atas 3 adalah 6
0
1
2
3
4
5
Penjumlahan atas 5 adalah 15
Menggunakan
Variabel Outer Pada Metode Anonim
Variabel lokal
atau parameter yang mempunyai skop mencakup sebuah metode anonim dikenal dengan
variabel outer. Metode anonim mempunyai akses terhadap dan dapat menggunakan
variabel outer. Ketika variabel outer digunakan oleh metode anonim, variabel
tersebut dikatakan sebagai ditangkap.
Variabel yang tertangkap tetap eksis selama delegate yang menangkapnya belum
dihancurkan. Jadi, meskipun variabel lokal biasanya dihancurkan ketika kendali
program keluar dari blok variabel tersebut, jika variabel lokal tersebut sedang
digunakan oleh sebuah metode anonim, maka variabel itu akan tetap eksis selama
delegate yang menunjuk metode tersebut belum dihancurkan.
Penangkapan
variabel lokal dapat menyebabkan hasil yang tidak diinginkan. Sebagai contoh,
perhatkan versi program ini. Pada versi sebelumnya, penjumlahan atas hitung
dilakukan. Namun, pada versi ini, objek Hitung
diciptakan dan dijadikan nilai balik oleh sebuah metode static, Penghitung().
Objek ini menggunakan variabel jum,
yang dideklarasikan di dalam skop tertutup yang disediakan oleh Penghitung(). Oleh karena itu, jum
ditangkap oleh metode anonim. Di dalam Main(),
Penghitung() dipanggil untuk
mendapatkan sebuah objek Hitung. jum akan dihancurkan ketika program
berhenti.
// Demonstrasi variabel yang
ditangkap.
using System;
// Delegate ini menghasilkan
nilai balik dan menerima argumen int.
delegate
int
Hitung(int akhir);
class VarTangkap
{
static
Hitung Penghitung()
{
int
jum = 0;
// Di sini, penjumlahan atas hitung
disimpan
// di dalam variabel jum yang
ditangkap.
Hitung hitungObj = delegate (int akhir) {
for(int i=0; i <=
akhir; i++) {
Console.WriteLine(i);
jum += i;
}
return jum;
};
return
hitungObj;
}
static
void Main() {
// Mendapatkan penghitung.
Hitung hitung = Penghitung();
int
hasil;
hasil = hitung(3);
Console.WriteLine("Penjumlahan atas 3 adalah " +
hasil);
Console.WriteLine();
hasil = hitung(5);
Console.WriteLine("Penjumlahan atas 5 adalah " +
hasil);
}
}
Keluaran program
ditampilkan sebagai berikut:
0
1
2
3
Penjumlahan atas 3 adalah 6
0
1
2
3
4
5
Penjumlahan atas 5 adalah 21
Seperti yang
dapat Anda lihat, penghitungan tetap berjalan normal. Namun, perhatikan nilai penjumlahan
untuk 5, yang menghasilkan 21 bukan 15!. Alasannya adalah bahwa jum ditangkap oleh hitungObj ketika ia diciptakan oleh metode Penghitung(). Ini berarti variabel jum tetap eksis sampai hitung dihancurkan di akhir program.
Jadi, nilainya tidak dihancurkan ketika Penghitung()
selesai dieksekusi atau ketika setiap kali metode anonim dipanggil manakala hitung dipanggil di dalam Main().
Ekspresi
Lambda
Di dalam sebuah
ekspresi lambda, ekspresi di sisi kanan operator lambda => memakai parameter
yang dispesifikasi di sisi kiri. Hasil ekspresi menjadi hasil dari operator
lambda dan dijadikan nilai balik.
Berikut adalah
bentuk umum dari ekspresi lambda yang hanya mengambil satu parameter:
param => ekspr
Ketika lebih
dari satu parameter diperlukan, maka bentuk berikut digunakan:
(daftar-param) => ekspr
Oleh karena itu,
ketika dua atau lebih parameter diperlukan, semuanya harus diapit oleh sepasang
kurung. Jika tidak ada parameter yang dibutuhkan, maka kurung kosong tetap
diperlukan.
Berikut adalah
ekspresi lambda sederhana:
hitung => hitung + 2
Di sini, hitung adalah parameter yang dipakai
pada ekspresi hitung + 2. Jadi,
hasil adalah nilai dari hitung
ditambah dengan dua. Ini adalah contoh lain:
n => n % 2 == 0
Pada kasus ini,
ekspresi ini akan menghasilkan true
jika n genap dan false jika n ganjil.
Penggunaan
ekspresi lambda melibatkan dua tahap. Pertama, mendeklarasikan sebuah tipe
delegate yang kompatibel dengan ekspresi lambda. Kedua, mendeklarasikan sebuah
instans delegate dan menugaskan ekspresi lambda kepadanya. Ketika hal ini telah
dilakukan, ekspresi lambda dapat dieksekusi dengan memanggil instans delegate.
Hasil dari ekspresi lambda menjadi nilai balik.
Program berikut
menunjukkan bagaimana melihat dua ekspresi lambda digunakan. Program
mendeklarasikan dua tipe delegate. Pertama, dinamakan Inkr, mengambil sebuah argumen int
dan menghasilkan nilai balik bertipe int.
Kedua, dinamakan ApaGenap, mengambil
sebuah argumen int dan menghasilkan
nilai balik bertipe bool. Program kemudian
menugaskan ekspresi lambda kepada instans dari kedua delegate. Terakhir,
program mengeksekusi ekspresi lambda melalui instans delegate.
// Menggunakan dua ekspresi
lambda.
using System;
// Mendeklarasikan sebuah
delegate yang mengambil argumen int
// dan nilai balik bertipe
int.
delegate
int Inkr(int v);
// Mendeklarasikan sebuah
delegate yang mengambil argumen int
// dan nilai balik bertipe
bool.
delegate bool ApaGenap(int v);
class DemoLambdaSederhana {
static
void Main() {
// Menciptakan instans delegate Inkr
yang menunjuk ke
// sebuah ekspresi lambda yang menambah
parameternya sebesar 2.
Inkr inkr = hitung => hitung + 2;
// Sekarang, menggunakan ekspresi lambda inkr.
Console.WriteLine("Menggunakan ekspresi lambda inkr:
");
int
x = -10;
while(x
<= 0) {
Console.Write(x + " ");
x = inkr(x); // menambah x dengan 2
}
Console.WriteLine("\n");
// Menciptakan instans delegate
ApaGenap yang menunjuk ke
// sebuah ekspresi lambda yang
menghasilkan true jika parameternya
// genap dan false jika ganjil.
ApaGenap apaGenap = n => n % 2 == 0;
// Sekarang, menggunakan ekspresi
lambda apaGenap.
Console.WriteLine("Menggunakan ekspresi lambda apaGenap:
");
for(int i=1; i <= 10; i++)
if(apaGenap(i)) Console.WriteLine(i + " adalah genap.");
}
}
Keluaran program
ditampilkan di sini:
Menggunakan ekspresi lambda inkr:
-10 -8 -6 -4 -2 0
Menggunakan ekspresi lambda apaGenap:
2 adalah genap.
4 adalah genap.
6 adalah genap.
8 adalah genap.
10 adalah genap.
Pada program,
perhatikan khusus pada kedua deklarasi ini:
Inkr inkr = hitung => hitung + 2;
ApaGenap apaGenap = n => n % 2 == 0;
Event
Fitur C# yang
penting lainnya adalah event. Event merupakan pemberitahuan otomatis bahwa
suatu aksi telah terjadi. Event bekerja seperti ini: Ketika event terjadi,
semua handler yang teregister dipanggil. Handler event direperesentasikan oleh
delegate.
Event adalah
anggota sebuah kelas dan dideklarasikan menggunakan katakunci event. Bentuk umumnya adalah
event delegate-event nama-event;
Di sini, delegate-event adalah nama
delegate yang digunakan untuk mendukung event, dan nama-event adalah nama
objek event tertentu yang sedang dideklarasikan. Berikut akan disajikan sebuah
contoh event sederhana:
// Sebuah demonstrasi event sederhana.
using System;
// Deklarasi sebuah tipe
delegate untuk event.
delegate
void
EventHandlerKu();
// Deklarasik kelas yang
memuat event.
class EventKu
{
public
event EventHandlerKu SuatuEvent;
// Ini dipanggil untuk menyebabkan event.
public
void PadaSuatuEvent()
{
if
(SuatuEvent != null)
SuatuEvent();
}
}
class DemoEvent {
// Sebuah handler event.
static
void Handler() {
Console.WriteLine("Event terjadi");
}
static
void Main()
{
EventKu evt = new EventKu();
// Menambahkan Handler() ke daftar
event.
evt.SuatuEvent += Handler;
// Membangkitkan event.
evt.PadaSuatuEvent();
}
}
Program
menghasilkan keluaran:
Event terjadi
Meskipun
sederhana, program ini memuat semua elemen esensial untuk penanganan event.
Periksa hati-hati. Program dimulai dengan mendeklarasikan sebuah tipe delegate
untuk handler event, seperti ini:
delegate void EventHandlerKu();
Semua event
diaktivasi melalui sebuah delegate. Jadi, tipe delegate event mendefinisikan
tipe nilai balik dan sidik untuk event. Pada kasus ini, tidak ada parameter
yang diperlukan, meski parameter event diijinkan.
Berikutnya,
kelas event, dinamakan EventKu,
diciptakan. Di dalam kelas tersebut, sebuah event, SuatuEvent, dideklarasikan, menggunakan baris ini:
public event EventHandlerKu SuatuEvent;
Perhatikan
sintaksnya. Katakunci event
memberitahu kompiler bahwa sebuah event sedang dideklarasikan.
Yang juga
dideklarasikan di dalam EventKu
adalah metode PadaSuatuEvent(), yang
merupakan metode yang dipanggil program untuk memicu atau menyebabkan sebuah
event (yaitu, inilah metode yang dipanggil ketika event terjadi). Metode ini
memanggil sebuah handler event melalui delegate SuatuEvent, seperti ini:
if(SuatuEvent
!= null)
SuatuEvent();
Perhatikan bahwa
sebuah handler dipanggil jika dan hanya jika SuatuEvent tidak null. Untuk mencegah pemanggilan referensi null, delegate event harus diuji untuk
memastikan bahwa ia tidak null.
Di dalam DemoEvent, sebuah handler event, Handler(), diciptakan. Pada contoh
sederhana ini, handler event hanya menampilkan sebuah pesan. Di dalam Main(), sebuah objek EventKu diciptakan, dan Handler() diregister sebagai sebuah
handler untuk event ini, dengan mencantumkan:
EventKu evt = new
EventKu();
// Menambahkan Handler() pada daftar event.
evt.SuatuEvent += Handler;
Perhatikan bahwa
handler ditambahkan menggunakan operator +=.
Event hanya mendukung operator +=
dan -=. Pada kasus ini, Handler() merupakan sebuah metode static, tetapi sebenarnya handler event
bisa saja berupa metode instans.
Terakhir, event
dipicu dengan:
// Memicu event.
evt.PadaSuatuEvent();
Pemanggilan PadaSuatuEvent() menyebabkan semua
handler event yang teregistrasi dipanggil. Pada kasus ini, hanya satu handler
yang teregistrasi.
Contoh
Event Multicast
Seperti
delegate, event juga bisa multicast. Ini memampukan banyak objek untuk merespon
sebuah pemberitahuan event. Berikut adalah contoh event multicast:
// Demonstrasi multicast event.
using System;
// Deklarasi sebuah tipe delegate untuk event.
delegate void EventHandlerKu();
// Deklarasi sebuah kelas yang memuat event.
class EventKu
{
public event EventHandlerKu SuatuEvent;
// Ini dipanggil
untuk memicu.
public void PadaSuatuEvent()
{
if (SuatuEvent != null)
SuatuEvent();
}
}
class X
{
public void Xhandler()
{
Console.WriteLine("Event diterima oleh objek X");
}
}
class Y
{
public void Yhandler()
{
Console.WriteLine("Event diterima oleh objek Y");
}
}
class DemoEvent2 {
static void Handler() {
Console.WriteLine("Event diterima oleh DemoEvent");
}
static void Main() {
EventKu evt = new EventKu();
X xOb = new X();
Y yOb = new Y();
// Menambahkan
handler untuk daftar event.
evt.SuatuEvent
+= Handler;
evt.SuatuEvent
+= xOb.Xhandler;
evt.SuatuEvent
+= yOb.Yhandler;
// Memicu
event.
evt.PadaSuatuEvent();
Console.WriteLine();
// Menghapus
sebuah handler.
evt.SuatuEvent
-= xOb.Xhandler;
evt.PadaSuatuEvent();
}
}
Keluaran program
ditampilkan di sini:
Event diterima oleh DemoEvent
Event diterima oleh objek X
Event diterima oleh objek Y
Event diterima oleh DemoEvent
Event diterima oleh objek Y
Metode
Instans Sebagai Handler Event
Meskipun metode
instans dan metode static dapat
dipakai sebagai handler event, tetapi keduanya berbeda dalam cara. Ketika
metode static dipakai sebagai
handler, pemberitahuan event berlaku pada kelas. Ketika metode instans
digunakan sebagai handler, event dikirim ke instans objek tertentu. Jadi, setiap
objek suatu kelas yang ingin menerima notifikasi event harus diregister secara
individual. Pada kenyataannya, kebanyakan handler event adalah metode instans.
Program berikut
menciptakan sebuah kelas X yang
mendefinisikan suatu metode instans sebagai handler event. Ini berarti bahwa
setiap objek X harus mendaftarkan
secara individual untuk menerima event. Untuk mendemonstasikannya, program
melakukan multicast event pada tiga objek bertipe X.
// Metode instans sebagai
handler event
using System;
// Mendeklarasikan tipe
delegate untuk sebuah event.
delegate
void
EventHandlerKu();
// Mendeklarasikan kelas
yang memuat sebuah event.
class EventKu {
public
event EventHandlerKu SuatuEvent;
// Ini dipanggil untuk memicu event.
public
void PadaSuatuEvent()
{
if
(SuatuEvent != null)
SuatuEvent();
}
}
class X
{
int
id;
public
X(int x) { id = x; }
// Ini adalah metode instans yang akan
dipakai sebagai handler event.
public
void Xhandler()
{
Console.WriteLine("Event diterima oleh objek " + id);
}
}
class DemoEvent3
{
static
void Main()
{
EventKu evt = new EventKu();
X o1 = new X(1);
X o2 = new X(2);
X o3 = new X(3);
evt.SuatuEvent += o1.Xhandler;
evt.SuatuEvent += o2.Xhandler;
evt.SuatuEvent += o3.Xhandler;
// Memicu event.
evt.PadaSuatuEvent();
}
}
Keluaran program
ditampikan di sini:
Event diterima oleh objek 1
Event diterima oleh objek 2
Event diterima oleh objek 3
No comments:
Post a Comment