C#

Memahami Penyimpanan Method di C#: Delegate, Action, Func, dan Predicate

Cara Menyimpan Method di C# dengan Delegate, Action, Func

AKID MAULANA AS SHIDIQ

AKID MAULANA AS SHIDIQ

Penulis

27 Apr 2026
12 x dilihat

Pengantar: Method sebagai Objek di C#

Dalam dunia pemrograman berorientasi objek, kita terbiasa memperlakukan data (variabel, properti) sebagai objek yang dapat disimpan, dimanipulasi, dan diteruskan. Namun, bagaimana jika kita bisa melakukan hal yang sama terhadap perilaku atau instruksi itu sendiri? Di C#, hal ini sangat mungkin dilakukan, dan ini adalah salah satu fitur paling kuat yang membuka pintu ke berbagai pola desain canggih. Konsep ini dikenal sebagai "first-class citizens" untuk method, di mana method dapat diperlakukan layaknya objek.

Salah satu cara utama untuk menyimpan dan mereferensikan method di C# adalah melalui penggunaan Delegate. Selain itu, .NET Framework menyediakan delegate generik yang lebih praktis seperti Action, Func, dan Predicate yang dapat menyederhanakan kode Anda.

Apa itu Delegate?

Secara fundamental, delegate adalah sebuah tipe yang merepresentasikan referensi ke sebuah method dengan signature (tipe kembalian dan parameter) yang spesifik. Bayangkan delegate sebagai sebuah "pointer" yang aman terhadap tipe (type-safe pointer) ke sebuah method.

Mengapa kita membutuhkan delegate?

  • Passing Methods as Arguments: Memungkinkan Anda meneruskan method sebagai parameter ke method lain. Ini adalah dasar dari banyak callback, event, dan LINQ.
  • Storing Methods: Menyimpan referensi ke method untuk dipanggil nanti.
  • Asynchronous Programming: Digunakan dalam beberapa skenario pemrograman asinkron.

Mendefinisikan dan Menggunakan Delegate

Langkah-langkah untuk menggunakan delegate adalah sebagai berikut:

  1. Deklarasikan Delegate: Tentukan signature method yang ingin Anda referensikan.
  2. Instansiasi Delegate: Buat sebuah instance dari delegate dan arahkan ke method yang sesuai.
  3. Panggil Method Melalui Delegate: Gunakan instance delegate untuk memanggil method yang direferensikannya.

Contoh Deklarasi Delegate:

// Deklarasi delegate yang mereferensikan method tanpa parameter dan tanpa nilai kembalian
public delegate void MyDelegate();

// Deklarasi delegate yang mereferensikan method dengan dua parameter integer dan mengembalikan integer
public delegate int CalculatorDelegate(int a, int b);

Contoh Penggunaan Delegate:

using System;

public class DelegateExample
{
    // Method yang akan direferensikan
    public static void Greet()
    {
        Console.WriteLine("Hello from Greet method!");
    }

    public static int Add(int x, int y)
    {
        return x + y;
    }

    public static int Subtract(int x, int y)
    {
        return x - y;
    }

    public static void Main(string[] args)
    {
        // 1. Deklarasi Delegate
        // MyDelegate greetDelegate; // Ini adalah cara lama, lebih disukai menggunakan lambda atau 'var'
        // CalculatorDelegate calcDelegate;

        // 2. Instansiasi Delegate
        // Menghubungkan delegate ke method Greet
        var greetDelegate = new MyDelegate(Greet); // Cara eksplisit
        var greetDelegate2 = Greet; // Cara singkat (type inference)

        // Menghubungkan delegate ke method Add
        var calcDelegate = new CalculatorDelegate(Add); // Cara eksplisit
        var calcDelegate2 = Add; // Cara singkat (type inference)

        // 3. Panggil Method Melalui Delegate
        Console.WriteLine("Calling Greet method via delegate:");
        greetDelegate(); // Memanggil Greet()
        greetDelegate2(); // Memanggil Greet()

        Console.WriteLine("\nPerforming calculations:");
        int sum = calcDelegate(10, 5); // Memanggil Add(10, 5)
        Console.WriteLine($"Sum: {sum}");

        // Kita bisa mengganti method yang direferensikan oleh delegate
        Console.WriteLine("Switching calculator delegate to Subtract:");
        calcDelegate = Subtract; // Mengganti referensi ke method Subtract
        int difference = calcDelegate(10, 5); // Memanggil Subtract(10, 5)
        Console.WriteLine($"Difference: {difference}");

        // Delegate juga bisa menunjuk ke method instance (non-static)
        var instance = new DelegateExample();
        var instanceMethodDelegate = new MyDelegate(instance.InstanceGreet);
        instanceMethodDelegate();
    }

    public void InstanceGreet()
    {
        Console.WriteLine("Hello from instance method!");
    }
}

// Definisi delegate di luar class jika diperlukan global
public delegate void MyDelegate();
public delegate int CalculatorDelegate(int a, int b);

Delegate dengan Lambda Expressions

Sejak C# 3.0, lambda expressions menyediakan cara yang lebih ringkas untuk membuat instance delegate, terutama ketika Anda hanya membutuhkan method tersebut sekali atau ketika methodnya sederhana.

using System;

public class LambdaDelegateExample
{
    public static void Main(string[] args)
    {
        // Tanpa lambda:
        // CalculatorDelegate addDelegate = Add;
        // int sum = addDelegate(10, 5);

        // Dengan lambda:
        CalculatorDelegate addDelegate = (x, y) => x + y;
        int sum = addDelegate(10, 5);
        Console.WriteLine($"Sum using lambda: {sum}");

        // Delegate tanpa nilai kembalian
        var greetDelegate = () => Console.WriteLine("Hello from lambda!");
        greetDelegate();
    }

    // Method Add tetap sama jika diperlukan di tempat lain
    public static int Add(int x, int y)
    {
        return x + y;
    }
}

// Definisi delegate
public delegate int CalculatorDelegate(int a, int b);

Delegate Generik: Action, Func, dan Predicate

.NET Framework menyediakan delegate bawaan yang lebih umum dan seringkali lebih praktis, sehingga Anda tidak perlu mendeklarasikan delegate kustom untuk banyak skenario.

Action Delegate

Action delegate digunakan untuk method yang tidak mengembalikan nilai (void). Terdapat beberapa overload Action, Action<T1>, Action<T1, T2>, hingga Action<T1, ..., T16> untuk method yang menerima hingga 16 parameter.

  • Action: Untuk method tanpa parameter.
  • Action<T>: Untuk method dengan satu parameter bertipe T.
  • Action<T1, T2>: Untuk method dengan dua parameter bertipe T1 dan T2.
  • Dan seterusnya.

Contoh Penggunaan Action:

using System;

public class ActionExample
{
    public static void DisplayMessage(string message)
    {
        Console.WriteLine($"Message: {message}");
    }

    public static void PrintSum(int a, int b)
    {
        Console.WriteLine($"Sum is: {a + b}");
    }

    public static void Main(string[] args)
    {
        // Menggunakan Action untuk method tanpa parameter
        Action greetAction = () => Console.WriteLine("Hello!");
        greetAction();

        // Menggunakan Action<string> untuk method dengan satu parameter string
        Action<string> displayAction = DisplayMessage;
        displayAction("This is a test message.");

        // Menggunakan Action<int, int> untuk method dengan dua parameter integer
        Action<int, int> printSumAction = PrintSum;
        printSumAction(20, 30);

        // Menggunakan lambda expression dengan Action
        Action<string, int> showInfo = (name, age) =>
        {
            Console.WriteLine($"Name: {name}, Age: {age}");
        };
        showInfo("Alice", 25);
    }
}

Func Delegate

Func delegate digunakan untuk method yang mengembalikan nilai. Func selalu memiliki setidaknya satu parameter, dan parameter terakhir selalu mendefinisikan tipe kembalian.

  • Func<TResult>: Untuk method tanpa parameter yang mengembalikan TResult.
  • Func<T, TResult>: Untuk method dengan satu parameter T yang mengembalikan TResult.
  • Func<T1, T2, TResult>: Untuk method dengan dua parameter T1 dan T2 yang mengembalikan TResult.
  • Dan seterusnya, hingga 16 parameter input.

Contoh Penggunaan Func:

using System;

public class FuncExample
{
    public static int Multiply(int a, int b)
    {
        return a * b;
    }

    public static string GetGreeting(string name)
    {
        return $"Hello, {name}!";
    }

    public static void Main(string[] args)
    {
        // Menggunakan Func<int, int, int> untuk method yang mengembalikan int dari dua int
        Func<int, int, int> multiplyFunc = Multiply;
        int product = multiplyFunc(7, 6);
        Console.WriteLine($"Product: {product}");

        // Menggunakan Func<string, string> untuk method yang mengembalikan string dari string
        Func<string, string> greetingFunc = GetGreeting;
        string greetingMessage = greetingFunc("Bob");
        Console.WriteLine(greetingMessage);

        // Menggunakan Func tanpa parameter (hanya tipe kembalian)
        Func<DateTime> getCurrentTime = () => DateTime.Now;
        Console.WriteLine($"Current time: {getCurrentTime()}");

        // Menggunakan lambda expression dengan Func
        Func<double, double, double> divideFunc = (num1, num2) =>
        {
            if (num2 == 0) return 0; // Penanganan sederhana
            return num1 / num2;
        };
        Console.WriteLine($"Division: {divideFunc(10, 2)}");
        Console.WriteLine($"Division by zero: {divideFunc(10, 0)}");
    }
}

Predicate Delegate

Predicate adalah delegate generik spesifik yang digunakan untuk method yang mengembalikan nilai boolean (bool) dan menerima satu parameter input. Ini adalah alias untuk Func<T, bool>.

Contoh Penggunaan Predicate:

using System;
using System.Collections.Generic;
using System.Linq;

public class PredicateExample
{
    public static bool IsEven(int number)
    {
        return number % 2 == 0;
    }

    public static bool IsLongString(string text)
    {
        return text.Length > 5;
    }

    public static void Main(string[] args)
    {
        // Menggunakan Predicate<int> untuk method yang mengembalikan bool dari int
        Predicate<int> evenCheck = IsEven;
        Console.WriteLine($"Is 4 even? {evenCheck(4)}"); // True
        Console.WriteLine($"Is 7 even? {evenCheck(7)}"); // False

        // Menggunakan Predicate<string>
        Predicate<string> longStringCheck = IsLongString;
        Console.WriteLine($"Is 'programming' long? {longStringCheck("programming")}"); // True
        Console.WriteLine($"Is 'short' long? {longStringCheck("short")}");       // False

        // Menggunakan Predicate dengan lambda expression
        Predicate<int> isPositive = (num) => num > 0;
        Console.WriteLine($"Is 10 positive? {isPositive(10)}"); // True
        Console.WriteLine($"Is -5 positive? {isPositive(-5)}"); // False

        // Contoh penerapan di List<T>.FindAll()
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        List<int> evenNumbers = numbers.FindAll(isEven); // Menggunakan delegate Predicate yang sudah ada
        Console.WriteLine("Even numbers: " + string.Join(", ", evenNumbers));

        List<string> words = new List<string> { "apple", "banana", "cherry", "date", "elderberry" };
        List<string> longWords = words.FindAll(IsLongString); // Menggunakan delegate Predicate yang sudah ada
        Console.WriteLine("Long words: " + string.Join(", ", longWords));

        // Menggunakan lambda langsung di FindAll
        List<int> oddNumbers = numbers.FindAll(n => n % 2 != 0);
        Console.WriteLine("Odd numbers: " + string.Join(", ", oddNumbers));
    }
}

Kapan Menggunakan Delegate Kustom vs. Delegate Generik?

  • Gunakan Delegate Kustom:

    • Ketika Anda membutuhkan kejelasan eksplisit dalam signature method yang direferensikan, terutama untuk library yang akan digunakan oleh orang lain.
    • Ketika Anda perlu mendefinisikan event. event di C# secara implisit menggunakan delegate.
  • Gunakan Delegate Generik (Action, Func, Predicate):

    • Dalam sebagian besar kasus, terutama saat bekerja dengan lambda expressions atau LINQ.
    • Ketika Anda ingin kode lebih ringkas dan mudah dibaca untuk skenario umum.
    • Saat parameter dan tipe kembalian sudah jelas dari konteksnya.

Tips Praktis yang Jarang Diketahui Pemula

  1. Operator += dan -= untuk Event Handling: Delegate mendukung penjumlahan (+=) dan pengurangan (-=) untuk mengelola koleksi handler (metode) yang akan dipanggil ketika event terjadi. Ini adalah cara kerja event di C#.

    public event EventHandler MyEvent; // EventHandler adalah delegate generik
    
    // Menambahkan handler
    MyEvent += HandlerMethod1;
    MyEvent += HandlerMethod2;
    
    // Memanggil semua handler yang terdaftar
    MyEvent?.Invoke(this, EventArgs.Empty);
    
    // Menghapus handler
    MyEvent -= HandlerMethod1;
    
  2. Perbedaan Antara Action dan Func dalam Konteks LINQ: Meskipun keduanya bisa merepresentasikan method, Action digunakan untuk operasi yang memiliki efek samping (misalnya, mencetak ke konsol, memodifikasi objek) dan tidak mengembalikan nilai yang digunakan untuk operasi berikutnya. Func digunakan untuk operasi yang menghasilkan nilai yang akan diproses lebih lanjut.

  3. var dan Delegate: Saat menggunakan lambda expression, kompiler C# sangat baik dalam menginferensikan tipe delegate yang sesuai. Seringkali Anda tidak perlu secara eksplisit menyebutkan tipe delegate jika menggunakan var. Namun, untuk kejelasan, terutama pada API publik, eksplisit menyebutkan tipe delegate terkadang lebih baik.

  4. Delegate Multicast: Delegate secara alami mendukung konsep "multicast". Anda dapat menggabungkan beberapa method ke dalam satu delegate. Ketika delegate multicast dipanggil, semua method yang terkait dengannya akan dipanggil secara berurutan. Ini digunakan dalam sistem event.

    public delegate void MyMultiDelegate();
    
    public void Method1() { Console.WriteLine("Method 1"); }
    public void Method2() { Console.WriteLine("Method 2"); }
    
    MyMultiDelegate multiDel = Method1;
    multiDel += Method2; // Menambahkan Method2 ke delegate
    multiDel(); // Akan memanggil Method1 lalu Method2
    
    multiDel -= Method1; // Menghapus Method1
    multiDel(); // Hanya akan memanggil Method2
    
  5. ?.Invoke() untuk Keamanan: Ketika memanggil delegate (terutama event delegate), disarankan untuk menggunakan operator kondisional ?.Invoke() untuk menghindari NullReferenceException jika tidak ada handler yang terdaftar.

Kesimpulan

Memahami cara menyimpan dan mereferensikan method melalui delegate di C# adalah kunci untuk menguasai pola pemrograman yang lebih dinamis dan fleksibel. Dari delegate kustom yang mendefinisikan signature spesifik, hingga delegate generik Action, Func, dan Predicate yang menyederhanakan banyak skenario umum, C# memberikan Anda kekuatan untuk memperlakukan method sebagai objek pertama-warga. Penguasaan konsep ini akan sangat meningkatkan kemampuan Anda dalam membangun aplikasi yang lebih canggih, reusable, dan efisien.

0.0

Berikan Rating

Komentar (0)

Silakan login untuk memberikan komentar.

Login Sekarang

Belum ada komentar. Jadilah yang pertama!