จากประสบการณ์ที่เคยผมโปรแกรมโดยใช้ Develop Tools มาหลายตัว (ไม่ขอเอ่ยชื่อนะครับเดี๋ยวจะหาว่าขี้โม้
อิอิ ) การเขียน โปรแกรมให้สั้น และกระชับที่สุด เป็นสิ่งจำเป็นอย่างมาก
ทั้งนี้เพื่อให้ผู้พัฒนา โปรแกรมสามารถ อ่านทำความเข้าใจ กับคำสั่ง ได้ง่ายขึ้น
เนื่องจากคำสั่งที่ต้องแยกแยะ หรือตีความมีน้อย ในบทความนี้ ผมจะแนะนำ การเขียนโปรแกรม
โดยนำหลักการ Dynamic Cast มาประยุกต์ใช้ ดูกันที่ละตัวเลยนะครับ
จากรูปจะเห็นว่ามี Component
วางอยู่บน Form ทั้งหมด 5 ตัว สมมติว่าผมต้องการให้ทุกตัวตอบรับ OnClickEvent
ผมสามารถทำได้หลายๆ วิธีดังนี้
- กำหนดให้ทุกตัวเรียกใช้ OnClickEvent
แยกคนละตัว
- ให้ทุกเรียกใช้ OnClickEvent ของตัวใดตัวนึง
ซึ่งหมายความว่าให้มีการเรียกใช้ฟังก์ชั่นเพียงตัวเดียวทำให้ประหยัดเนื้อที่หน่วยความไปได้เยอะ
- ดักจับ Message โดยประยุกต์ใช้
static function มาตรฐาน เช่น WinMain, UMSpecialMessage, WindowProc ฯลฯ
คอยดักจับ Message แล้ว Broadcast คำสั่งส่งไปให้ Component อีกทีนึง ฟังก์ชั่นที่กล่าวมาทั้งหมดนี้
ความจริงมีอยู่หลายตัวครับ แต่ส่วนใหญ่จะถูกซ่อนเอา ถ้าต้องการใช้ก็ต้องไป
Declare เอง เอาไว้โอกาสหน้าผมจะเขียนเป็นบทความให้อ่านกันนะครับ
- ประยุกต์ใช้วิธีการต่าง ๆ ที่กล่าวมาโดยใช้หลักการ
Dynamic Cast ที่ต้องแยกเป็นอีกหัวข้อเพราะว่าบางทีคุณอาจจะอ้างถึงตัวแปรหรือ
Component นั้นโดยตรงก็ได้
- ฯลฯ
ในบทความนี้เรามาพูดถึง Dynamic Cast
กันนะครับ ใน Project ด้านบนที่ Main Form ของเรา สมมติคุณกำหนดให้ Component
ทุกตัวยกเว้นคอมโพเน้นท์ Label1 มีการเรียกใช้ OnClickEvent ตัวเดียวกันในที่นี้คือ
Button1Click โดยใน Button1Click เป็นคำสั่งให้แสดงชื่อ Component ที่ถูก
Click ให้เขียนคำสั่งดังนี้
?
void __fastcall TForm1::Button1Click(TObject
*Sender)
{
if(dynamic_cast< TButton * >(Sender))
{
Label1->Caption =
"คุณเลือก" + dynamic_cast< TButton * >(Sender)->Name;
}
else if(dynamic_cast< TButton * >(Sender))
{
Label1->Caption = "คุณเลือก" + dynamic_cast< TEdit * >(Sender)->Name;
}
}
?
จากตัวอย่างข้างบนผมใช้คำสงวนหรือเรียกว่า C++-Specific Keywords อย่าเข้าใจว่าตัวนี้เป็น
Function นะครับ (โดยเนื้อแท้มันก็คือฟังก์ชั่นนั่นแหละครับ) พวกนี้เป็นคำสงวนที่มีอยู่ใน
C++ เวลาเราเขียนโปรแกรม ห้ามตั้งชื่อ โดยใช้ชื่อคำสงวน เหล่านี้ คำสงวนเหล่าได้แก่
if ? else, switch .. case, int , void , __fastcall ฯ ดังนั้น dynamic_cast
จึงเป็น คำสงวน หรือ keyword นั่นเอง
ในตัวอย่างข้างบน เราใช้คำสงวน dynamic_cast
เพื่อตรวจตรวจว่า Sender ในที่นี้คือ TObject * ว่าเป็น TButton * หรือไม่
ถ้าใช่ก็ให้เข้ามาค่าที่ส่งกลับมาจะไม่ใช่ค่า NULL ดูตามคำสั่งข้างล่างในคำสั่ง
if(dynamic_cast< TButton * >(Sender)) นะครับ จากนั้นถ้าเป็นไปตามเงื่อนไขก็ให้
Label1 แสดงชื่อ Component ที่ถูกเลือกนั้น
if(dynamic_cast< TButton * >(Sender))
{
Label1->Caption =
"คุณเลือก" + dynamic_cast< TButton * >(Sender)->Name;
}
จากตัวอย่างที่ผ่านมาคุณอาจจะสงสัยว่าถ้า
if(dynamic_cast< TButton * >(Sender)) ตรวจสอบค่าว่าถ้าไม่ใช่ค่า
NULL แสดงว่ามีการเลือก TObject แล้วทำการ cast อัตโนมัติตามนี้เลยได้มั้ย
void __fastcall TForm1::Button1Click(TObject
*Sender)
{
Label1->Caption =
"คุณเลือก" + dynamic_cast< TButton * >(Sender)->Name;
}
คำตอบคือไม่ได้ครับเพราะว่า TObject ไม่ได้มีแต่ TButton เท่านั้น ในที่นี้เป็นได้ทั้ง
TEdit ด้วย จากตัวอย่างเมื่อคุณทดลอง Run โปรแกรมโปรแกรมจะแสดง error ถ้าคุณ
Click เม้าส์ที่ TEdit ที่ error ก็เพราะว่าคำสั่ง dynamic_cast< TButton
* >(Sender) ได้ค่า retrun ค่า NULL กลับคืนมา นั่นหมายความว่าไม่มีการ
Click ที่ Object ใด แต่จริง ๆ แล้วคุณ Click ที่ TEdit (ห้ามงงนะครับ) จากนั้นคำสั่ง
สั่งให้แสดงชื่อหรือ Name ของ Object ซึ่งก็คือ dynamic_cast< TButton
* >(Sender)->Name; ให้โปรแกรมแสดงข้อผิดพลาดออกมา
เมื่อกี้เราใช้คำสงวน dynamic_cast เพื่อทำการ Cast คำสั่งแล้วนะครับ ต่อไปเรามาดูอีกตัวอย่างนึงนะครับเป็นการ
Dynamic Cast เหมือนกัน
void __fastcall TForm1::Button1Click(TObject
*Sender)
{
if( Sender == Button1 || Sender == Button2 || Sender == Button3 )
{
Label1->Caption =
"คุณเลือก" + dynamic_cast< TButton * >(Sender)->Name;
}else if( Sender == Edit1)
{
Label1->Caption = "คุณเลือก " + dynamic_cast < TEdit *
>(Sender)->Name;
}
}
จากตัวอย่างใช้การตรวจสอบ Handle
ของ Sender ว่ามีค่าเท่ากับ Object ที่เราต้องการหรือไม่ในที่นี้คือ if(
Sender == Button1 || Sender == Button2 || Sender == Button3 ) และ else
if( Sender == Edit1) ถ้าใช่ให้ทำคำสั่งที่เราระบุ ในตัวอย่างนี้เรายังใช้คำสงวนอยู่นะครับ
มาดูตัวอย่างอีกตัวนึงนะครับ
void __fastcall TForm1::Button1Click(TObject
*Sender)
{
if(((TButton *)(Sender)))
{
Label1->Caption = "คุณเลือก" + ((TButton *)(Sender))->Name;
}
}
จากตัวอย่างข้างบนจะเห็นว่าผมทำการตรวจสอบก่อนว่า
Sender ไม่ใช่ค่า NULL จากนั้นถ้าใช่ ให้ Label1 แสดงชื่อ Name ของ Object
ที่ถูก Click โดยการใช้ Cast โดยการระบุชื่อ Object โดยตรงในที่นี้ก็คือคำสั่ง
((TButton *)(Sender)) จากตัวอย่างผู้อ่านอาจจะสงสัยว่าทำไมผมไม่ตรวจสอบก่อนว่า
Object ที่ถูก Click เป็น TButton หรือ TEdit ด้วยหละ คำตอบก็คือในที่นี้
Name Property หรือ ชื่อของ Object ที่เราใช้มันใช้หลักการของ Virtual Function
ครับ
การใช้คำสั่ง ((TButton *)(Sender))->Name เป็นการอ้างถึง Property ชื่อ
Name การเรียกใช้งานคำสั่ง นี้คุณต้องแน่ใจว่า Object ที่คุณเรียกใช้มี Property
ชื่อว่า Name อยู่ด้วยนะครับ มิเช่นนั้นจะเกิดข้อผิดพลาดขึ้นได้ครับ จากตัวอย่างข้างบน
ผมสามารถแก้ไขได้ดังนี้ครับ ออกจะยาวนิดนึง
if(((TButton *)(Sender)))
{
if( Sender->ClassNameIs("TButton"))
Label1->Caption = "คุณเลือก" + ((TButton *)(Sender))->Name;
else if( Sender->ClassNameIs("TEdit"))
Label1->Caption = "คุณเลือก" + ((TEdit *)(Sender))->Name;
}
หรือจะใช้คำสั่งดังนี้ก็ได้
if(((TButton *)(Sender)))
{
if(Sender->ClassNameIs(Button1->ClassName()))
Label1->Caption = "คุณเลือก" + ((TButton *)(Sender))->Name;
else if(Sender->ClassNameIs(Edit1->ClassName()))
Label1->Caption = "คุณเลือก" + ((TEdit *)(Sender))->Name;
}
|