Delegate adalah tipe yang menampung referensi ke sebuah metode, sehingga metode dapat disimpan pada variabel, diteruskan sebagai argumen, dan dipanggil melalui nama variabelnya. Func dan Action adalah delegate generik bawaan .NET yang menghapus kebutuhan mendeklarasikan tipe delegate sendiri, yaitu Func untuk metode yang mengembalikan nilai dan Action untuk metode yang tidak. Topik ini membahas keduanya serta lambda, yaitu fungsi ringkas tanpa nama, sebagai argumen metode, dan memperlihatkan perannya di balik operator LINQ.
Func untuk metode dengan nilai kembali#
Func<TResult> sampai Func<T1, T2, ... , TResult> menampung metode yang mengembalikan nilai, dan parameter tipe terakhirnya selalu menyatakan tipe nilai kembali, sedangkan parameter tipe sebelumnya menyatakan tipe argumen secara berurutan. Variabelnya dipanggil persis seperti metode biasa. Contoh berikut menampung dua lambda pada variabel Func lalu memanggilnya.
// Contoh: Func menampung lambda kuadrat dan setengah
Func<int, int> kuadrat = n => n * n;
Func<double, double> setengah = n => n / 2;
Console.WriteLine(kuadrat(7));
Console.WriteLine(setengah(9.0));49
4.5Func<int, int> dibaca sebagai metode yang menerima satu int dan mengembalikan satu int, sedangkan Func<double, double> menerima dan mengembalikan double. Lambda pada sisi kanan penugasan menyediakan badan metodenya tanpa deklarasi kelas maupun nama. Variabel delegate kemudian dapat dipanggil berulang kali dan diteruskan ke metode lain seperti nilai biasa.
Isi variabel Func tidak harus berupa lambda, karena metode biasa pun dapat ditugaskan lewat konversi method group, misalnya Func<int, int> f = Kuadrat ketika metode statis bernama Kuadrat tersedia. Keduanya berperilaku sama pada saat dipanggil, dan lambda biasanya dipilih untuk logika pendek yang hanya digunakan di satu tempat.
// Contoh: Func dua parameter untuk menilai kelulusan terhadap ambang
Func<int, int, bool> lolosAmbang = (nilai, ambang) => nilai >= ambang;
Console.WriteLine(lolosAmbang(80, 75));
Console.WriteLine(lolosAmbang(60, 75));True
FalseFunc<int, int, bool> menampung metode dengan dua parameter int yang mengembalikan bool, dan lambda dengan dua parameternya ditulis (nilai, ambang) => nilai >= ambang. Tabel berikut merangkum pola pembacaan parameter tipenya.
| Tipe delegate | Dibaca sebagai |
|---|---|
Func<int> | tanpa argumen, mengembalikan int |
Func<int, int> | satu argumen int, mengembalikan int |
Func<int, int, bool> | dua argumen int, mengembalikan bool |
Func<string, int> | satu argumen string, mengembalikan int |
Action untuk prosedur tanpa nilai kembali#
Action dan Action<T1, T2, ...> menampung metode yang tidak mengembalikan apa pun, sehingga padanannya adalah metode bertipe kembali void. Kegunaannya paling terasa untuk prosedur berbasis efek samping, misalnya pencatatan dan pencetakan. Contoh berikut menampung prosedur pencatatan pada variabel Action<string> lalu memanggilnya dua kali.
// Contoh: Action menampung prosedur pencatatan tanpa nilai kembali
Action<string> catat = pesan => Console.WriteLine($"[CATAT] {pesan}");
catat("Proses dimulai");
catat("Proses selesai");[CATAT] Proses dimulai
[CATAT] Proses selesaiPerbedaan utama dengan Func terletak pada parameter tipenya, karena Action<string> hanya menyatakan satu argumen string dan tidak ada posisi untuk nilai kembali. Satu variabel delegate juga dapat diganti isinya dengan lambda lain, sehingga prosedur yang dipanggil pada satu titik kode dapat ditukar pada saat program berjalan. Parameter Action tersedia pula dalam bentuk dua argumen dan seterusnya, misalnya Action<string, int> untuk prosedur yang menerima pesan beserta tingkat kepentingannya.
Lambda sebagai argumen metode#
Kombinasi Func dan lambda paling sering muncul sebagai argumen metode, yaitu metode penerima memutuskan kapan lambda dipanggil, sedangkan pemanggil menentukan isi pekerjaannya. Operator LINQ bekerja persis dengan pola ini, karena Where dan Select adalah metode biasa yang parameternya bertipe delegate. Contoh berikut menulis metode sendiri yang parameternya bertipe Func<string, string>.
// Contoh: metode menerima Func sebagai argumen transformasi
string TerapkanKeSemua(string[] kata, Func<string, string> transformasi)
{
return string.Join(" ", kata.Select(transformasi));
}
string[] kata = { "penghitung", "kata" };
Console.WriteLine(TerapkanKeSemua(kata, k => k.ToUpperInvariant()));
Console.WriteLine(TerapkanKeSemua(kata, k => $"{k}({k.Length})"));PENGHITUNG KATA
penghitung(10) kata(4)Metode TerapkanKeSemua tidak mengetahui bentuk transformasinya, karena keputusan itu diserahkan kepada argumen lambda pada setiap pemanggilan. Pemanggil pertama mengirim lambda kapitalisasi, sedangkan pemanggil kedua mengirim lambda penghitung panjang, dan satu metode yang sama melayani keduanya. Pada baris kata.Select(transformasi), lambda yang diterima diteruskan lagi kepada operator Select, sehingga rantai penerusan argumen delegate terbentuk secara alami.
Lambda bukan tipe tersendiri, melainkan ekspresi yang diubah oleh kompilator menjadi objek delegate, sehingga penugasannya selalu menuntut tipe sasaran yang jelas. Karena itu, deklarasi var catat = pesan => Console.WriteLine(pesan) ditolak kompilator, sedangkan Action<string> catat = pesan => Console.WriteLine(pesan) sah. Kaidah ini berlaku sama pada argumen metode, yaitu tipe parameternya yang menyatakan bentuk delegate yang diharapkan.
Lambda dengan lebih dari satu pernyataan ditulis dengan badan berblok, yaitu kurung kurawal beserta pernyataan return yang eksplisit, dan tipenya tetap Func atau Action yang sama. Bentuk panjang ini lazim ketika satu langkah transformasi memerlukan perhitungan sementara atau percabangan di dalamnya. Dalam operator LINQ, lambda tersebut diterima sebagai parameter bertipe delegate, sehingga pengetahuan pada topik ini berlaku langsung pada query sebelumnya.
Contoh program utuh#
Program berikut merangkum Func, Action, dan lambda sebagai argumen metode dalam satu berkas.
// Program contoh Func, Action, dan lambda sebagai argumen metode
// Func: lambda dengan nilai kembali
Func<int, int> kuadrat = n => n * n;
Func<double, double> setengah = n => n / 2;
Console.WriteLine($"Kuadrat dari 7: {kuadrat(7)}");
Console.WriteLine($"Setengah dari 9: {setengah(9.0)}");
// Func dua parameter: penilaian terhadap ambang
Func<int, int, bool> lolosAmbang = (nilai, ambang) => nilai >= ambang;
Console.WriteLine($"80 lolos ambang 75: {lolosAmbang(80, 75)}");
Console.WriteLine($"60 lolos ambang 75: {lolosAmbang(60, 75)}");
// Action: prosedur tanpa nilai kembali
Action<string> catat = pesan => Console.WriteLine($"[CATAT] {pesan}");
catat("Proses dimulai");
catat("Proses selesai");
// Lambda sebagai argumen metode buatan sendiri
string[] kata = { "penghitung", "kata" };
Console.WriteLine(TerapkanKeSemua(kata, k => k.ToUpperInvariant()));
Console.WriteLine(TerapkanKeSemua(kata, k => $"{k}({k.Length})"));
string TerapkanKeSemua(string[] kata, Func<string, string> transformasi)
{
return string.Join(" ", kata.Select(transformasi));
}Kuadrat dari 7: 49
Setengah dari 9: 4.5
80 lolos ambang 75: True
60 lolos ambang 75: False
[CATAT] Proses dimulai
[CATAT] Proses selesai
PENGHITUNG KATA
penghitung(10) kata(4)Jalankan program tersebut dari folder topik dengan perintah berikut.
# aktifkan .NET yang terpasang di sandbox
export DOTNET_ROOT=/home/kal/.dotnet PATH=/home/kal/.dotnet:$PATH DOTNET_CLI_TELEMETRY_OPTOUT=1 DOTNET_NOLOGO=1
# jalankan program contoh dari folder topik
dotnet run --file Program.csKuadrat dari 7: 49
Setengah dari 9: 4.5
80 lolos ambang 75: True
60 lolos ambang 75: False
[CATAT] Proses dimulai
[CATAT] Proses selesai
PENGHITUNG KATA
penghitung(10) kata(4)Kesalahan umum#
- Menyusun parameter tipe
Funcsecara terbalik, misalnyaFunc<int, string>untuk lambda yang menerimastring. Parameter tipe terakhir adalah nilai kembalinya, sehingga urutan yang benar adalahFunc<string, int>dan susunan yang salah ditolak kompilator. - Memanggil variabel delegate yang belum diisi nilainya. Exception
NullReferenceExceptionmuncul pada runtime, dan perbaikannya adalah memastikan variabel tersebut sudah diisi sebelum dipanggil. - Menambahkan
returndengan nilai pada lambda yang dideklarasikan sebagaiAction. Tipe kembaliannyavoid, sehingga lambda tersebut hanya berisi pernyataan efek samping dan penolakannya berasal dari kompilator.
Ringkasan#
Funcmenampung metode yang mengembalikan nilai, dan parameter tipe terakhirnya selalu menyatakan tipe nilai kembali.Actionmenampung prosedur tanpa nilai kembali, dengan parameter tipenya murni menyatakan argumen.- Lambda menyediakan badan metodenya secara ringkas, dan variabel delegate dipanggil seperti metode biasa.
- Pola lambda sebagai argumen metode digunakan oleh operator LINQ pada
WheredanSelect, sehingga metode buatan sendiri dapat menerima perilaku yang diserahkan pemanggilnya.
Referensi#
- Lambda expressions — Microsoft Learn — Rujukan resmi sintaks lambda dan konversinya ke delegate.
- Func
— Microsoft Learn — Rujukan keluarga delegateFuncbeserta aturan parameter tipenya. - Action
— Microsoft Learn — Rujukan keluarga delegateActionyang tidak mengembalikan nilai.