ตอน 30 Operator overloading และ delegate

ไปหน้าแรก | สารบัญ | Laploy.comระเบียนบทความ | บทความจากลาภลอย

เว็บไซต์นี้เป็นตัวอย่างเนื้อหาบางตอนในหนังสือ "เรียนรู้ด้วยตนเอง OOP C# ASP.NET" ครอบคลุม บทที่ 1 ถึงบทที่ 6 (ในหนังสือมี 21 บท) เนื้อหาใน Blog อาจอาจแตกต่างจากในหนังสือเพราะเป็นเนื้อหาที่ยังไม่ได้ตรวจแก้ขัดเกลา (edit)

กดที่นี่เพื่อดูรายละเอียดเนื้อหาในแต่ละบท

กดที่นี่เพื่อไปยังเว็บบอร์ด ถาม-ตอบ 

 

Operator overloading

operator overloading คือการเปลี่ยนหน้าที่ของ operator ให้มีความหมายใหม่ เพื่อใช้งานกับ type ที่เราสร้างขึ้นเอง มีประโยชน์เพราะทำให้เขียนโค้ดใช้งาน type ของเราได้ง่ายขึ้น ได้โค้ดสะอาดเรียบร้อยอ่านง่ายกว่า ภาษา C# สนับสนุนการทำ operator overloading เช่นเดียวกับภาษา C++ ขณะที่ภาษา Java และ VB.NET 1.0 ไม่สนับสนุน

ภาษา C# มี operator หรือตัวกระทำ เช่น + (บวก) – (ลบ) ^ (exclusive or) เพื่อทำหน้าที่กระทำระหว่าง operand (ตัวถูกกระทำ) เช่นในนิพจน์ x + y x และ y คือ operand และ + คือ operator โดย operator เหล่านี้เป็น operator แบบ built-in ที่ถูกนิยามไว้แล้วในตัวภาษา C# เอง ภาษา C# ยินยอมให้เรานิยาม operator ใหม่ขึ้นใช้เองได้ หรือจะนำ operator แบบ built-in มาเปลี่ยนแปลงเพื่อให้ทำหน้าที่อื่นก็ได้ เรียกว่า operator overloading

operator overloading ทำให้เขียนโค้ดได้กระชับขึ้น โปรดพิจารณาตัวอย่างต่อไปนี้


Number foo = new Number();
foo.bar = 10;
foo = foo + 5;

สมมุติว่าเราสร้าง type ใหม่ชื่อ Number (บรรทัดที่ 1) ขึ้น โดยต้องการให้ type นี้เก็บค่า int (บรรทัดที่ 2) และต้องการให้ object ของ type นี้สามารถใช้งานกับ operator + ได้ราวกับมันคือตัวแปร int ตัวหนึ่ง (บรรทัดที่ 3) ก็สามารถทำได้โดยนิยามคลาส Number ดังนี้

1    public class Number
2    {
3       public int bar;
4       public static Number operator +(Number num, int x)
5       {
6          num.bar = num.bar + x;
7          return num;
8       }
9    }

โค้ดข้างบนคือการนิยามคลาสชื่อ Number ซึ่งจะมีการทำ operator overload บรรทัดที่ 3 เรานิยาม field สมาชิกแบบ int เพื่อทำหน้าที่เก็บข้อมูลในคลาสนี้ บรรทัดที่ 4 ถึงบรรทัดที่ 8 คือ method ที่ทำหน้าที่ overload โปรดสังเกตว่า method นี้มีชื่อเดียวกับคลาส (บรรทัดที่ 4) ทำให้มันคล้าย constructor เราจึงอาจจะมองว่ามันเป็น constructor ของการทำ operator overload ก็ได้

โปรดสังเกตต่อไปว่าเราใช้ keyword operator และเครื่องหมาย + เป็นตัวประกาศว่าเราจะทำ overload operator กับเครื่องหมาย + พารามิเตอร์ในวงเล็บคือ operand ของ operator นี้ ในที่นี้เรากำหนดให้ operand ทางซ้ายมี type เป็น Number และ operand ทางขวามี type เป็น int

ภายใน method มีโค้ดสั่งให้นำค่าที่ได้รับมาเป็น argument มาบวกกัน (บรรทัดที่ 6) โดยเป็นการบวกค่าของ field สมาชิก num ของคลาส Number กับค่า integer x และส่งค่ากลับไปเป็น type เดียวกับ operand ทางซ้ายคือ Number (บรรทัดที่ 7)

ข้อสรุปเรื่อง operator overloading
• ใช้เพื่อเปลี่ยนหน้าที่ของ operator
• ใช้ร่วมกับ type ที่เรานิยามขึ้นใหม่
• การนิยามใช้ keyword operator ร่วมกับตัว operator ที่ต้องการ
• ทำได้กับ operator ทุกตัว ยกเว้น = . ?: -> new is sizeof และ typeof

 

Delegate

delegate คือ type ชนิดพิเศษ ความพิเศษของมันคือขณะที่ตัวแปร type อื่นๆ ใช้ทำหน้าที่เก็บข้อมูล (ยกตัวอย่างเช่น int foo ตัวแปร foo ถูกกำหนดให้มี type เป็น int จึงเก็บข้อมูลแบบเลขจำนวนเต็มได้) แต่ตัวแปร delegate ใช้เก็บการอ้างอิงไปยัง method

ผู้สร้าง .NET Framework สร้างกลไก delegate ขึ้นเพราะ delegate มีความจำเป็นในหลายๆ สถานการณ์ เช่น

  • multi-thread: ในภาษา C# เราสามารถวิ่งหลายๆ โปรเซสไปพร้อมๆ กันได้เรียกว่า thread เมื่อจะเริ่ม thread ใหม่เราจำเป็นต้องระบุ method ที่จะให้ไปทำงานโดยอาศัย delegate
  • Library: เมื่อสร้างคลาสไลบรารีบ่อยครั้งที่คลาสจำเป็นต้องเรียกกลับไปยังคลาสผู้ขอใช้บริการ (class client) ซึ่งทำได้โดยอาศัย delegate เท่านั้น
  • Event: โปรแกรมที่ทำงานบนระบบปฏิบัติการ Windows จะพบ event ตลอดเวลา (event-driven programming) เช่นเมื่อผู้ใช้คลิกเมาส์จะมี event Mouse_Click เกิดขึ้น การกำหนดว่าจะให้ไปทำงาน method ใดเมื่อเกิด event จำเป็นต้องอาศัย delegate

delegate ช่วยให้การอ้างถึง method ทางอ้อมสามารถทำได้ ภาษาที่เน้นการใช้งาน pointer อย่างภาษา C++ จะใช้ pointer เพื่อชี้ไปยัง function (ฟังก์ชันในภาษา C++ คือ method ในภาษา C#) เรียกว่า function pointer จึงอาจกล่าวได้ว่า delegate คือ function pointer แบบ OOP และมีคุณสมบัติ type-safety

delegate ทำให้ object สามารถเรียกหา (call back) หรือส่งสัญญาณ (โดยใช้ร่วมกับ event) แจ้งไปยังโค้ดที่ใช้งานมัน เพื่อให้ดำเนินการบางอย่าง เมื่อเกิดเหตุการณ์พิเศษขึ้น ยกตัวอย่างเช่นเรานิยามคลาสชื่อ AlarmClock ทำหน้าที่เป็นนาฬิกาปลุก และนิยามคลาสชื่อ Class1 ซึ่งสร้าง object ชื่อ myAlarm จากคลาส AlarmClock ดังตัวอย่างโค้ดต่อไปนี้

    class AlarmClock
    {
    }
    class Class1
    {
        static void Main(string[] args)
        {
            AlarmClock myAlarm = new AlarmClock();
        }
    }

การทำงานลักษณะนี้คลาส AlarmClock ถือว่าเป็นผู้ให้บริการ (class server) และคลาส Class1 ถือว่าเป็นผู้รับบริการ (class client) ในภาษา C# class client อ้างถึง class server ได้เพราะมีตัวอ้างอิง (ในที่นี้คือตัวแปร แบบ reference ชื่อ myAlarm) เพื่อใช้ติดต่อกับ server ได้ตลอดเวลา แต่ class server ไม่มีตัวอ้างอิงสำหรับใช้เรียกหา class client ได้จึงไม่สามารถ call back ไปที่ class client ได้

ภาษา OOP ที่ใช้ pointer เป็นหลักอย่างภาษา C++ โค้ดส่วน class server สามารถจะใช้ pointer เพื่อเป็นตัวอ้างอิงกลับไปยัง function ที่เรียกใช้งานมันได้ (คือ function pointer) แต่ภาษา C# เมื่อทำงานในแบบ managed code ไม่อาจใช้ pointer ได้ ผู้สร้างภาษา C# จึงประดิษฐ์ delegate ขึ้นเพื่อแก้ปัญหานี้ ต่อไปนี้เป็นโค้ดตัวอย่างแสดงการสร้างและใช้งาน delegate โดยยกตัวอย่างเป็นนาฬิกาปลุกตามที่กล่าวข้างต้น

1    public class AlarmClock
2    {
3       public delegate int ACEventHandler(int i);
4       public event ACEventHandler AlarmNow;
5       private void TimeOut()
6       {
7          AlarmNow(12);
8       }
9    } 

นี้คือโค้ดส่วนที่จะทำหน้าที่เป็น class server ในตัวอย่างนี้ คือนิยามคลาสนาฬิกาปลุกนั่นเอง บรรทัดที่ 3 คือส่วนประกาศ delegate ซึ่งมีส่วนประกอบต่างๆ ห้าส่วนดังนี้

   ส่วนที่    ชื่อหน้าที่ โค้ดในตัวอย่าง

1.     access modifier: public
2.     type declaration: delegate
3.     return type: int
4.     type name: ACEventHandler
5.     parameter: (int i)

รายละเอียดของส่วนประกอบต่างๆ แต่ละส่วนเป็นดังนี้ ส่วนที่ 1 ต้องเป็น public เสมอเพื่อให้มองเห็น (หรือรับรู้) ได้จาก class client ส่วนที่ 2 เป็นการประกาศ type แบบ reference type ที่จะใช้ห่อหุ้ม (encapsulate) method แบบ anonymous

anonymous method เป็น method แบบใหม่ที่เริ่มมีใช้ใน C#2.0 ถูกสร้างมาเพื่ออำนวยความสะดวกในการประกาศ delegate เพราะก่อนหน้า C#2.0 การประกาศ delegate ต้องอ้างถึง method จริงๆ ที่ต้องสร้างเตรียมไว้ต่างหาก ทำให้การเขียนโค้ดยืดเยื้อ แต่ anonymous method ช่วยให้ไม่ต้องสร้าง method จริงๆ ขึ้น เพียงประกาศส่วนหัวของ method ไว้ก็พอ

ส่วนที่ 3 คือ return type ที่จะกำหนดให้เป็น type อะไรก็ได้ตามความต้องการ ส่วนที่ 4 คือชื่อ type ของ delegate นี้ และส่วนสุดท้าย 5 คือพารามิเตอร์ของค่าที่เราต้องการส่งไปยัง class server เมื่อเกิด call back

บรรทัดที่ 4 ประกาศ event ชื่อ AlarmNow เป็น event ที่จะทำหน้าที่ call back ไปยัง class server เมื่อต้องการทำ delegate โปรดสังเกตว่าต้องเรากำหนดให้ AlarmNow มี type ตรงกับ delegate type ที่ประกาศไว้ข้างบน ดังนั้น AlarmNow จะมี return type และพารามิเตอร์ตรงกับ delegate นี้

ต่อมาเรานิยาม method ชื่อ TimeOut() ซึ่งจะถูกเรียกให้ทำงานเมื่อนาฬิกาเดินมาถึงจุดที่ตั้งไว้ (ไม่ได้แสดงในโค้ดตัวอย่าง) ภายใน method นี้มีคำสั่งเพียงบรรทัดเดียวคือสั่งที่ทำหน้าที่ปลุกให้ delegate ทำงานนั่นคือการอ้างถึง event ชื่อ AlarmNow นั่นเอง โปรดสังเกตว่า AlarmNow จะต้องส่งค่าพารามิเตอร์เหมือนที่นิยามไว้ใน delegate โดยในโค้ดนี้พารามิเตอร์มี int เพียงตัวเดียว

เมื่อจัดเตรียมโค้ด delegate ไว้ที class server แล้ว class client ก็ต้องเตรียมโค้ดส่วนรับ delegate ไว้ด้วยซึ่งเป็นเพียงการรับ event ดังนี้

10    class Class1
11    {
12       static void Main(string[] args)
13       {
14          AlarmClock myAlarm = new AlarmClock();
15          myAlarm.AlarmNow +=new ACEventHandler(myAlarm_AlarmNow);
16       }
17       private static int myAlarm_AlarmNow(int i)
18       {
19          return i;
20       }
21    } 

โค้ดข้างบนคือนิยามคลาสชื่อ Class1 ทำหน้าที่เป็น class client บรรทัดที่ 14 สร้าง object ชื่อ myAlarm จากคลาส AlarmClock บรรทัดที่ 15 คือการลงทะเบียนรับ event ชื่อ AlarmNow ที่จะมาจาก myAlarm บรรทัดที่ 17 คือ method ทำหน้าที่ให้บริการเมื่อเกิด event โปรดสังเกตว่า method นี้ต้องมีพารามิเตอร์และ return type ตรงกับ delegate ที่ประกาศไว้ในคลาส AlarmClock

ข้อสรุปเรื่อง delegate

• ช่วยให้ทำ call back ในภาษา C# ได้
• ทดแทนการใช้ function pointer
• ควรใช้ delegate ร่วมกับ anonymous method (C#2.0 ขึ้นไป)เพื่อให้โค้ดกระชับ
• delegate เป็น reference type ชนิดหนึ่ง
• delegate อาจใช้ร่วมกับ event เพื่อทำ call back ก็ได้
• method ตัวที่รับ event ต้องมี signature ตรงกับที่นิยามไว้ใน delegate

ตอนต่อไป: Implicitly typed local variables

Post a comment or leave a trackback: Trackback URL.

ใส่ความเห็น

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / เปลี่ยนแปลง )

Twitter picture

You are commenting using your Twitter account. Log Out / เปลี่ยนแปลง )

Facebook photo

You are commenting using your Facebook account. Log Out / เปลี่ยนแปลง )

Google+ photo

You are commenting using your Google+ account. Log Out / เปลี่ยนแปลง )

Connecting to %s

%d bloggers like this: