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# และการใช้งานในสภานการณ์ต่างๆ ซึ่งสามารถอำนวยความสะดวกในการออกแบบโปรแกรม


บทความเกี่ยวกับ Delegates

Delegates ในภาษา Visual Basic

Delegate เป็นประเภทข้อมูลที่ใช้ในการอ้างถึงฟังก์ชันหรือเมธอด บางทีเราเรียก delegate ว่าเป็นพอยน์เตอร์ของฟังก์ชัน ในการใช้ delegate จะทำให้เราสามารถทำงานกับฟังก์...