Delegates

ในบทนี้ เราจะพูดเกี่ยวกับ Delegates และวิธีการใช้งานกับเมธอดในภาษา C#

Delegates คืออะไร

Delegates เป็นตัวแปรประเภท reference type ที่ถูกพัฒนาขึ้นมาสนับสนุนการเขียนโปรแกรมในภาษา C# มันใช้สำหรับในการอ้างถึงข้อมูลอื่น แทนที่จะอ้างถึงออบเจ็ค มันอ้างถึงเมธอดแทน หรือกล่าวอีกนัยหนึ่ง delegates คือพอยน์เตอร์ของเมธอดนั่นเอง

การใช้ delegates นั้นช่วยอำนวนความสะดวกหลายอย่าง เช่น การเลือกเมธอดที่จะทำงานใน run time การนำกลับมาใช้ใหม่ของโค้ด นอกจากนี้ delegates ยังทำให้สามารถส่งเมธอดเป็นพารามิเตอร์ได้ อย่างไรก็ตามปราศจาก delegates เราก็ยังสามารถเรียกใช้เมธอดแบบปกติได้

การใช้ delegates

ต่อไปเราจะมีตัวอย่างสำหรับการประกาศและใช้งาน delegates ในภาษา C#

using System;

class DelegatesExample
{
    delegate void MyDelegates();

    static void Main(string[] args)
    {
        MyDelegates de = new MyDelegates(Callback);
        de();

        de = Callback2;
        de();
    }

    static void Callback () {
        Console.WriteLine("Called by Delegate.");
    }

    static void Callback2()
    {
        Console.WriteLine("Second method, called by Delegate.");
    }
}

ตัวอย่างข้างบน เราได้ประกาศ delegate type ขึ้นมา โดย delegate นี้จะสามารถทำงานได้กับเมธอดที่มีการส่งค่าลกับเป็น void และไม่มีพารามิเตอร์

delegate void MyDelegates();

ในเมธอด Main() เราได้สร้างตัวแปร delegate เพื่อทำมาใช้เมธอดประเภทดังกล่าว และใส่พารามิเตอร์เป็น Callback () สำหรับ delegate constructor ตอนนี้ตัวแปร delegate de อ้างถึงเมธอดดังกล่าว และเราเรียกใช้เมธอด

MyDelegates de = new MyDelegates(Callback);
de();

ต่อมาเราได้กำหนดให้ delegate อ้างไปยังเมธอด Callback2() และเรียกใช้เมธอดอีกครั้ง ในคำสั่ง

de = Callback2;
de();

เพราะว่าเมธอด Callback() และ Callback2() เป็นเมธอดที่มีการส่งกลับเป็น void และไม่มีพารามิเตอร์ ดังนั้นมันจึงสามารถใช้ได้กับ delegate นี้

Called by Delegate.
Second method, called by Delegate.

และนี่เป็นผลลัพธ์ของโปรแกรมในการใช้งานเมธอดกับ delegate

คุณสามารถประกาศ delegate แบบสั้นได้ โดยไม่ต้องใส่เมธอดลงไปใน constructor ซึ่งเป็นอีกวิธีที่จะอำนวยความสะดวกในการเขียนโปรแกรม ยกตัวอย่างเช่น

MyDelegates de = Callback;

Delegates กับพารามิเตอร์และ return

ต่อไปเราจะมาสร้าง delegate กับพารามิเตอร์ เพื่อส่งตัวแปรเข้าไปทำงานในเมธอด เช่นเดียวกันกับเมธอด delegate สามารถส่งค่ากลับ มีประเภทหรือจำนวนของพารามิเตอร์ที่แตกต่างกันได้

using System;

class DelegatesExample2
{
    delegate string MyDelegates(string s);

    static void Main(string[] args)
    {  
        Person p1 = new Person("Max", "Graham");

        MyDelegates de = p1.GreetFirstName;
        String name1 = de("Hey");

        de = p1.GreetLastName;
        String name2 = de("Hi");

        Console.WriteLine(name1);
        Console.WriteLine(name2);
    }
}

class Person {
    string firstName;
    string lastName;

    public Person (string f, string n) {
        firstName = f;
        lastName = n;
    }

    public string GreetFirstName (string s) {
        return s + " " + firstName;
    }

    public string GreetLastName(string s) {
        return s + " " + lastName;
    }
}

เหมือนเมธอด delegate สามารถส่งค่ากลับและมีพารามิเตอร์ ในตัวอย่างเราได้สร้าง delegate ที่มีการส่งค่ากลับเป็น string และมีพารามิเตอร์เป็น string หนึ่งตัว แต่ตอนนี้เราใช้กลับเมธอดของคลาส ในตัวอย่างเราได้ใช้ delegate เรียกใช้เพื่อสร้างประโยคทักทาย ว่าจากชื่อหรือนามสกุล

Hey Max
Hi Graham

และนี่เป็นผลลัพธ์ของโปรแกรม

Multicast delegate

ในการใช้งาน delegate กับเมธอดนั้น เราสามารถเพิ่มเมธอดเข้าไปยัง delegate เพื่อให้มันเรียกหลายๆ ครั้งได้ โดยการใช้ตัวดำเนินการ += เป็นการเพิ่มเมธอดใหม่ให้กับ delegate และ -= เป็นการนำเมธอดออกจาก delegate มาดูตัวอย่าง

using System;

class DelegatesExample3
{
    delegate void MyDelegates(int a, int b);

    static void Main(string[] args)
    {
        MyDelegates de;

        de = Add;
        de += Sub;
        de += Mul;
        de(5, 3);

        de -= Add;
        de(10, 8);
    }

    static void Add(int a, int b)
    {
        Console.WriteLine(a + " + " + b + " = " + (a + b));
    }

    static void Sub(int a, int b)
    {
        Console.WriteLine(a + " - " + b + " = " + (a - b));
    }

    static void Mul(int a, int b)
    {
        Console.WriteLine(a + " * " + b + " = " + (a * b));
    }
}

ในตัวอย่าง เราได้สร้าง delegate สำหรับใช้งานกับเมธอดที่มีค่าส่งกลับเป็น void และพารามิเตอร์สองตัวที่เป็น int ในตอนแรกเราได้เพิ่ม 3 เมธอดเข้าไปยัง delegate นั่นทำให้เมื่อเราเรียกใช้ delegate ในคำสั่ง de(5, 3); เมธอดทั้งหมกใน delegate ทำงานจากพารามิเตอร์ที่ส่งเข้าไป

de = Add;
de += Sub;
de += Mul;
de(5, 3);

ต่อมาเรานำเมธอด Add() ออกจาก delegate แล้วเรียกใช้อีกครั้ง

5 + 3 = 8
5 - 3 = 2
5 * 3 = 15
10 - 8 = 2
10 * 8 = 80

นี่เป็นผลลัพธ์ของโปรแกรม

Delegates เป็นพารามิเตอร์ของเมธอด

นอกจากนี้ ภาษา C# ยังสามารถให้ delegate ส่งเป็นพารามิเตอร์เข้ามายังเมธอดได้ มันอำนวยความสะดวกในการสร้าง callback เมธอด ที่เรียกใช้งานเมธอดโดยชื่อของมัน

using System;

class DelegatesExample4
{
    delegate void MyDelegates();

    static void Main(string[] args)
    {
        Callback(Method1);
        Callback(Method2);
    }

    static void Callback (MyDelegates m)
    {
        m();
    }

    static void Method1 () {
        Console.WriteLine("Call method 1");
    }

    static void Method2()
    {
        Console.WriteLine("Call method 2");
    }
}

ในตัวอย่าง เรามีเมธอด Callback () ซึ่งมีพารามิเตอร์เป็น delegate และเราได้เรียกใช้เมธอดที่ส่งเข้ามาภายในเมธอด Callback() แทน

Delegates กับอาเรย์

คุณสามารถเรียกใช้เมธอดแบบไดนามิกส์ได้ โดยเราสามารถสร้าง delegate ให้เป็นอาเรย์แล้วเรียกใช้ด้วย index ของมัน ซึ่ง delegate ก็เป็นออบเจ็คหนึ่งดังนั้นมันก็เหมือนกับอาเรย์ของออบเจ็คนั้นเอง

using System;

class DelegatesExample5
{
    delegate float MeDelegate(int[] n);

    static void Main(string[] args)
    {
        const int SIZE = 4;    

        int[] number = { 2, 8, 5, 10, -3, 4, -1, 7};
        float[] result = new float[SIZE];

        MeDelegate[] de = { FindMin, FindMax, FindSum, FindAvg };

        for (int i = 0; i < SIZE; i++) {
            result[i] = de[i](number);
        }

        Console.Write("Array: ");

        for (int i = 1; i < number.Length; i++)
        {
            Console.Write(number[i] + ", ");
        }

        Console.WriteLine("\nMin: " + result[0]);
        Console.WriteLine("Max: " + result[1]);
        Console.WriteLine("Sum: " + result[2]);
        Console.WriteLine("Avg: " + result[3]);
    }

    static float FindMin (int[] n)
    {
        int min = n[0];
        for (int i = 1; i < n.Length; i++) {
            if (n[i] < min)
                min = n[i];
        }
        return min;
    }

    static float FindMax(int[] n)
    {
        int max = n[0];
        for (int i = 1; i < n.Length; i++)
        {
            if (n[i] > max)
                max = n[i];
        }
        return max;
    }

    static float FindSum(int[] n)
    {
        int sum = 0;
        for (int i = 1; i < n.Length; i++)
        {
            sum += n[i];
        }
        return sum;
    }

    static float FindAvg (int[] n)
    {
        return ((float) FindSum(n)) / n.Length;
    }
}

ในตัวอย่าง เราได้สร้างอาเรย์ของ delegate นั่นหมายความว่าเราสามารถกำหนดเมธอดทั้งหมดของเราไว้ในอาเรย์ได้ เราได้สร้างฟังก์ชันสำหรับหาค่าน้อยสุด มากสุด ผลรวม และค่าเฉลี่ยจากอาเรย์ของตัวเลข และเราได้เรียกใช้ฟังก์ชันทั้งหมดผ่านทางอาเรย์ของ delegate

Array: 8, 5, 10, -3, 4, -1, 7,
Min: -3
Max: 10
Sum: 30
Avg: 3.75

นี่เป็นผลลัพธ์ของโปรแกรม

Anonymous methods

Anonymous methods คือเมธอดที่ไม่มีชื่อ โดยเมธอดจะสามารถเขียนแทรกลงในเมธอดอื่นได้ภายในโปรแกรม นี่เป็นตัวอย่างการสร้าง anonymous methods ในภาษา C#

using System;
using System.Threading;

class AnonymouseMothod
{
    delegate void MeDelegate();

    static void Main(string[] args)
    {
        // basic anomymous method
        MeDelegate de = delegate
        {
            Console.WriteLine("Anonymouse is invoked.");
        };
        de();

        // basic anomymous method with thread
        Thread t = new Thread(new ThreadStart(delegate
        {
            Console.WriteLine("Anonymouse method in thread.");
        }));

        t.Start();
    }
}

ในตัวอย่างเป็นการสร้าง anonymous method แบบธรรมดาโดย delegate และการสร้างสำหรับให้ thread ทำงาน และ anonymous method จะถูกใช้บ่อยๆ กับ event ซึ่งอำนวยความสะดวกและง่ายต่อการเขียนโปรแกรม ไม่ต้องแยกเมธอดไว้คนละที

ในบทนี้ คุณได้เรียนรู้เกี่ยวกับ delegate ในภาษา C# และการใช้งานในสภานการณ์ต่างๆ ซึ่งสามารถอำนวยความสะดวกในการออกแบบโปรแกรม