Delegates and Events ตอนที่ 2
ไม่รู้ว่าจะเรียกตอนที่ 2 ได้หรือป่าว แต่ก็เป็นตอนที่ต่อจาก ตอนที่แล้ว ยังคงเป็นตัวอย่างเพิ่มเติมอีกเพื่อให้มีความเข้าใจมาก มาก และ มากขึ้นอีก เนื่องจาก Concept ของ delegates นั้นเป็นเรื่องที่สำคัญมาก สำหรับการเขียนโปรแกรมในขั้น Advance ต่อไป จริง ๆ จะบอกให้ อยากให้เพื่อน ๆ หรือน้อง ๆ โปรแกรมเมอร์ ได้เข้าใจ จริง ๆ
เอาละครับเรามาเข้าเรื่องของเราเลยดีกว่า เราจะดูตัวอย่างของ delegates อีก 2 ตัวอย่าง โดยจะพิจารณาการทำงาน ทีละขั้น นะครับ พิจารณา code ต่อไปนี้ เป็นตัวอย่างต่อไป
namespace ConsoleApplication1
{
public class MyClass
{
public delegate void LogHandler(string message);
public void Process(LogHandler logHandler)
{
if (logHandler != null)
{
logHandler(“Process() begin”);
}
if (logHandler != null)
{
logHandler(“Process() end”);
}
}
}
public class TestApplication
{
// Static Function: To which is used in the Delegate. To call the process() function
static void Logger(string s)
{
Console.WriteLine(s);
}
static void Main(string[] args)
{
// Create an instance of the delegate , pointingto the logging function
// this delegate will then be passed to the Process() function.
MyClass myclass = new MyClass();
MyClass.LogHandler myLogger = new MyClass.LogHandler(Logger);
myclass.Process(myLogger);
}
}
}
จาก Code ข้างต้น หากเรา ทำการ Run ดู ก็จะได้ ผลการทำงาน เป็นอย่างนี้นะครับ
Process() begin
Process() end
ประมาณ นี้ เรามาลองพิจารณา Code กันดูนะครับ ว่า code ข้างต้นทำงานอย่างไร อย่าลืมว่าเรากำลัง ดูตัวอย่างการใช้งาน delegates ในตัวอย่างนี้ เราจะประกาศ หรือ declare delegates ไว้ใน class ชื่อ MyClass โดยให้ delegates มีชื่อว่า LogHandler โดยให้มี parameter 1 ตัว เป็น string ใช่ใหมครับ (บรรทัดที่ 5) เมื่อเราประกาศไว้เช่นนี้ นั่นก็หมายถึงว่า Function ที่เราต้องการ ทำ delegate ก็ต้องมี การประกาศในรูปแบบเดียวกัน นะครับ และจากตัวอย่างนี้ แสดงให้เห็น ว่า ตัว function อยู่ที่ไหน ก็ได้ใน namespace เดียวกัน ในตัวอย่างเราวางไว้ใน class TestApplication ซึ่งเป็น class ที่เราใช้เริ่มต้นการทำงาน
ในบรรทัด ที่ 22 – 25 เป็นส่วนที่เรา Implement function ที่เราต้องการให้เป็น delegates ซึ่งไม่ได้ทำอะไรมากเพียงแต่ นำ parameters ที่ส่งเขามา แสดงผลที่หน้าจอด้วย WriteLine() นะครับ สังเกตุว่า function นี้ เป็น อะไรครับ ใช้แล้วเป็น static function ด้วย หลายคนคงพอเข้าใจนะครับ ว่าทำไมต้องทำให้เป็นอย่างนั้น ถามว่า ไม่เป็น static ได้ไหม ตอบว่าได้ แต่ต้องเรียกใช้ผ่าน object ของ TestApplication ใช่ไหมครับ เพราะมัน เป็น static จึงเรียกใช้มันได้เลย นั่นแหละเป็น เหตุผล ครับ แต่ว่าตรงนี้คงไม่ใช้ประเด็น นะครับ มาดูกัน ครับ
บรรทัดที่ 7 – 18 คือ Function process มี Parameter 1 ตัว เป็นชนิด LogHandler ซึ่งมันคือ delegates type ที่เราประกาศไว้ เวลาเราใช้งาน delegate เรามองมันคล้าย class หรือ object ก็ได้นะครับ จะไ้ด้สะดวกใจ นั่นก็คือ function Process(LogHandler logHandler) นั้นรับ parameter เป็น delegate type นั่นเอง หลังจากนั้น ใน body เป็นการใช้งาน delegates ที่ส่งผ่านเข้าไป ซึ่งการเรียกใช้ ก็เป็นลักษณะนี้
logHandler("Process() begin");
ก็เป็นการส่ง string ให้กับ function นั่นแหละครับ มาดูต่อใน Function Main กันนะครับ ที่ function Main นี้เราจะเริ่มใช้งาน ด้วยการ instantiate MyClass หรือ สร้าง object ที่ชื่อ myClass ขึ่นมา และต่อมาเรา ก็ สร้าง instance ของ delegate โดยเรา ส่งชื่อ function เป็น argument
MyClass.LogHandler myLogger = new MyClass.LogHandler(Logger);
นั่นแหละครับเราก็จะได้ myLogger เป็น object ที่ชี้ไปที่ Logger ครับ เท่านี้เราก็พร้อมที่ส่ง มันไปทำงานที่ไหนก็ได้ตามต้องการ สำหรับตัวอย่างของเรา ส่งกลับไปเป็น Argument ของ myclass.Process() ดังนี้
myclass.Process(myLogger);
หลังจากนี้ผมคงไม่ต้อง อธิบายต่อนะครับว่ามันเป็นอย่างไรต่อไป
ครับ จากตัวอย่างคงพอจะเห็นการทำงานเพิ่มขึ้นนะครับ ว่า delegate ทำงานอย่างไร คิดว่าหลายคนยังคงมีคำถามหลาย ๆ คำถามอยู่ในใจ ผมคิดว่าหลาย ๆ คำถามอีกเช่นกัน ท่านสามารถทดลอง หาคำตอบได้ด้วยตนเอง ฮิ ฮิ คือว่าขี้เกียจตั้งคำถามอะครับ
อีกสักตัวอย่างเป็นไงครับ ดูตัวอย่างนี้ดีกว่านะครับ Advance ขึ้นมาอีก
using System.IO;
namespace ConsoleApplication1
{
public class MyClass
{
public delegate void LogHandler(string message);
public void Process(LogHandler logHandler)
{
if (logHandler != null)
{
logHandler(“Process() begin”);
}
if (logHandler != null)
{
logHandler(“Process() end”);
}
}
}
// The FileLogger class merely encapsulates the file I/O
public class FileLogger
{
FileStream fileStream;
StreamWriter streamWriter;
// Constructor
public FileLogger(string filename)
{
fileStream = new FileStream(filename, FileMode.Create);
streamWriter = new StreamWriter(fileStream);
}
// Member Function which is used in the Delegate
public void Logger(string s)
{
streamWriter.WriteLine(s);
}
public void Close()
{
streamWriter.Close();
fileStream.Close();
}
}
public class TestApplication
{
static void Main(string[] args)
{
FileLogger fl = new FileLogger(“process.log”);
MyClass myClass = new MyClass();
// Crate an instance of the delegate, pointing to the Logger()
// function on the fl instance of a FileLogger.
MyClass.LogHandler myLogger = new MyClass.LogHandler(fl.Logger);
myClass.Process(myLogger);
fl.Close();
}
}
}
เอาละครับ หลังจาก ดู code กันแล้วหลายคน คงพอเข้าใจ หลายคนคงถูกใจเพราะตรงกับคำถามในใจจาก ตัวอย่างที่แล้ว ครับในตัวอย่างนี้ เราสร้าง class ใหม่ขึ้นมานะครับ ชื่อว่า FileLogger แล้ว ใน class นี้เราก็มี method สำคัญของเรา คือ Logger เราย้ายมาไว้ใน class นี้เพื่อให้เห็นการทำงานที่แตกต่างออกไปอีก นั้นก็หมายถึงว่า ไม่ว่า function ที่ต้องการใช้เป็น delegate จะอยู่ที่ไหนก็ตาม เราก็สามารถใช้งานมันได้ ครับ ขอเพียงว่าให้เป็นไปตามข้อกำหนดของ การทำ delegates ก็แล้วกัน เอาล่ะครับ ตัวอย่างนี้คงไม่ต้อง พูดกันมากนะครับ เนื่องจากการประกาศ delegates นั้นใช้ของเดิมจากตัวอย่างที่แล้ว แต่ delegate funciton นั้น อยู่ใน class อีก class หนึ่งและก็ไม่ได้เป็น static method ด้วย ดังนั้นการที่จะเข้าถึง fuction Logger ก็ต้อง ทำผ่าน instance ของ class FileLogger นะครับ ดังนั้นใน Main method จึงได้ instantiate f1 ให้เป็น Object ของ FileLogger ขึ้นมา ซึ่งจะต้องสึ่งชื่อไฟล์ให้กับ Constructor ของ FileLogger ด้วยนะครับ ชื่อ ว่า process.log ตามนี้
FileLogger fl = new FileLogger(“process.log”);
เพื่อให้เราสามารถอ้างอิง เจ้า Logger ได้นั่นเอง หลังจากนั้น เราก็ส่ง f1.Logger ให้กับ delegates object ใช่ใหม ครับ แล้วเจ้า instance ของ delegates ก็อ้างอิงมายัง f1.Logger มันก็เหมือนเราเรียกใช้ f1.logger โดยการอ้างอิงผ่านทาง address นั่นเองครับ
MyClass.LogHandler myLogger = new MyClass.LogHandler(fl.Logger);
หลังจากนั้นเราก็ ส่ง myLogger ให้กับ Process เหมือนเดิมนะครับ แต่ผลการทำงานของ โปรแกรม นี้มันไม่ได้แสดงผลทรี่หน้าจอนะครับ มันส้ราง file และเขียนลง file ลอง พิมพ์ code แล้ว run ดูนะครับ แล้วจะเข้าใจ
จบก่อนนะครับ สำหรับ ตอนต่อไป จะเป็นเรื่อง Multicasting เน่อ 🙂