ในบทที่แล้ว เราเรียนรู้กันเรื่องการดูรายละเอียดเกี่ยวกับ Type ต่างๆ ในบทนี้เราจะมาแอบดูกันต่อว่า Type ต่างๆ ที่เราใช้กันอยู่ทุกวันนี้ ล้วนแล้วแต่ต้องมี physical ในการเก็บทั้งสิ้น และ physical นี้ก็คือแฟ้มข้อมูล .dll ที่เราเรียกกันว่า Assembly นั่นเอง แต่ก่อนที่เราจะเรียนรู้กันต่อ ผมขอแทรกอะไรหน่อยก่อน จะได้รู้ที่มา
Reflection เป็นแนวคิดที่มีมานานแล้ว แนวคิดหลักของการทำ Reflection ก็คือการเก็บโครงสร้างของ Type ต่างๆ แยกออกมาต่างหาก ซึ่งโครงสร้างเหล่านี้ เราเรียกว่า Metadata นั่นเอง แนวคิดของการแยก Metadata ออกมาต่างหากนั้น มีมานานแล้ว เท่าที่ผมสามารถไล่ย้อนไปได้ ก็น่าจะตั้งแต่ภาษา CLOS (Lisp version ที่เป็น OO) ภาษา C++ ไม่มีแนวคิด Reflection ในตัว ต้องหาบริการเสริมกันให้วุ่น ภาษาที่ใช้แนวคิด Reflection แล้วรุ่งก็คือภาษาตระกูล Visual ของ Microsoft และ Java ทั้งนี้ก็ใช้บริการของ COM และ CORBA นั่นเอง
ในเมื่อ COM มี Reflection จึงไม่น่าแปลกใจเลยว่า .NET ก็ต้องมี แต่คุณอาจจะสงสัยว่าประโยชน์ที่แท้จริงของ Reflection มันคือการที่ให้ Editor มีความสามารถทำ IntelliSense อย่างที่ผมเขียนในบทที่แล้วหรือ เปล่าเลยครับ นั่นคือผลพลอยได้ที่ทีมพัฒนาของ Microsoft ประยุกต์ใช้ แต่แท้ที่จริงแล้วประโยชน์ของ Reflection มันอยู่ที่คำว่า Persistent ครับ
Persistent คืออะไร คำนี้ผมสงสัยมานานแล้วเหมือนกัน คำนี้โผล่มาตอนแรกๆ ที่ผมหัดเขียนภาษา Progress และโผล่มาอีกทีเมื่อผมอ่านหนังสือ Object-Oriented Analysis and Design ของ ปรมาจารย์ Grady Booch ในหนังสือกล่าวไว้ว่า Persistent คือการที่ object สามารถถูกเก็บออกจาก Memory Space ที่มันอยู่ ผมเลยไปตีความเอาว่า คงจะประมาณว่า โปรแกรมรันอยู่ อาจจะมีความจำเป็นต้องปิดเครื่อง เราเลยต้องเอา objects ทุกตัวที่ทำงานอยู่ ไปเก็บพักใน harddisk พอพรุ่งนี้เช้า ค่อย Load มันออกมา มันจะได้ทำงานต่อจากเมื่อวานได้เลย ไม่ต้องเริ่มที่ 0 ใหม่
แบบนี้มีจริงๆ นะครับ มันเป็นเรื่อง Object Database Model ที่ทาง OMG พยายามจะปั้นอยู่ตอนนี้ Object Database เป็นแนวคิดที่ไม่ได้ใหม่นัก แต่ก็ถือว่าเป็นของใหม่ล่าสุด เพราะยังไม่มีคนใช้ แต่ต่อไป คงจะนิยมมาก มันแตกต่างจาก Relational Database ที่เราใช้ทุกวันนี้ค่อนข้างมาก ถ้าใช้เป็น เราจะได้ความสะดวกและประสิทธิภาพสูง แนวคิดของ Object Database ก็เป็นเหมือนที่ผมเข้าใจเข้า มันจะเก็บ Object แบบ Persistent นั่นเอง
เวลาเราเก็บ Object ลง Database นั้น ถ้าเราไม่เก็บ MetaDatabase ลงไปด้วย ข้อมูลที่เก็บก็จะเป็นเพียงแค่ขยะ พรุ่งนี้เช้ามาดึง object ก็จะไม่รู้ว่า Object ดังกล่าวมีหน้าตาอย่างไร จึงจำเป็นอยู่เองครับที่ต้องมีแนวคิด Reflection อยู่
คราวนี้ถ้าผมตีความคำของ Booch ใหม่ว่าการเอาออกจาก Memory Space ที่มันใช้งานอยู่ ไม่ได้เอาไปเก็บ แต่เอาไปรันที่ Memory Space อื่น เริ่มคุ้นแล้วใช้ไหมครับ มันก็คือแนวคิด N-tier ที่เราใช้อยู่ทุกวันนี้ทั้งในองค์กรและ Internet นั่นเอง เราสามารถเก็บ Object ไว้ที่เครื่องอื่น เวลาเราเรียกใช้ ขอให้เรารู้โครงสร้างของมัน เราก็สามารถเรียกใช้ได้เลย ถ้าจะเอาเป็นของใหม่ ก็เรื่อง Web Service มันมีการส่ง Metadata ออกมาให้เครื่อง Client ใช้เรียก ถ้าพูดอีกมุมหนึ่ง ก็พอจะบอกได้ว่า WSDL เป็น protocol ที่เอาไว้ส่งผ่าน Metadata ระหว่างเครื่องนั่นเอง
มาเข้าเรื่องของเราซักที ในอดีตของ Microsoft ตัว COM นั้นเก็บเฉพาะข้อมูล และ code ไม่มี Metadata ดังนั้นเพื่อให้รองรับแนวคิดของ Reflection ตัว COM จึงต้องเก็บ Metadata แยกต่างหากเป็นอีกแฟ้มข้อมูลหนึ่ง แต่พอมาเป็น .NET ทั้ง code และ Metadata อยู่ในแฟ้มเดียวกัน คือ .dll เลยครับ เรามาลองดู ผลลัพธ์ที่ผมแอบดู Assembly System.dll ดังนี้ครับ

จากรูปนี้เราจะเห็นได้ว่า ใน Assembly จะมีส่วนหนึ่งที่ชื่อว่า Manifest ซึ่งนั่นก็คือส่วนของ Metadata นั่นเอง Assembly ของ .NET จึงถือว่ามีความสมบูรณ์ในตัว ถ้าเราส่งผ่าน Internet ทำนอง Applet ผู้ใช้จะสามารถรู้โครงสร้างทั้งหมดได้ทันที ไม่ต้องพึ่งแฟ้มอื่นอีก
ในส่วนสุดท้ายเรามาแอบดู Assembly กัน ผมคงไม่อธิบายอะไรมากนัก เพราะคุณสามารถเปิด Help ได้อยู่แล้ว
|
ShowTypes.cs |
using System; using System.Reflection; class ShowTypes
{
public static void ShowTypes()
{
foreach( Type t in ass.GetTypes()) { Console.WriteLine(t.FullName); } }
public static void Main()
{
ShowMethods();
}
}
|
โปรแกรมนี้แสดง Types ทั้งหมดของ System.dll ดังนี้ครับ
|
DOS Prompt |
| C:\CS>
ShowTypes System.Diagnostics.Trace System.Collections.Specialized.ListDictionary System.Collections.Specialized.ListDictionary+NodeEnumerator System.Collections.Specialized.ListDictionary+NodeKeyValueCollection System.Collections.Specialized.ListDictionary+NodeKeyValueCollection+NodeKeyVal eEnumerator System.Collections.Specialized.ListDictionary+DictionaryNode System.Diagnostics.EventLogTraceListener System.ComponentModel.IContainer System.ComponentModel.Design.Serialization.IDesignerSerializationService System.CodeDom.CodeComment System.Net.BasicClient System.Net.Sockets.SocketOptionName System.ComponentModel.Design.IExtenderListService Microsoft.Win32.PowerModeChangedEventHandler System.Net.Dns System.Net.Dns+GetHostByNameDelegate System.Net.Dns+ResolveDelegate System.ComponentModel.Design.DesignerTransaction System.IO.FileSystemWatcher System.IO.Direct System.ComponentModel.DefaultEventAttribute System.Text.RegularExpressions.RegexParser System.ComponentModel.LocalizableAttribute System.ComponentModel.UInt16Converter ....
|
ในแต่ละบรรทัดที่เรา foreach() เราจะได้ type มา ตัวหนึ่ง เราสามารถที่ใช้ ShowMethods() ของบทที่แล้วมาแสดงรายละเอียดของแต่ละ Type ลองอ่าน Code ดูนะครับ ไม่น่าจะมีอะไรเข้าใจยาก
ถ้าใครสนใจรายละเอียดว่า เขาทำ
Reflection กันอย่างไร คุณสามารถศึกษาเพิ่มเติมได้ ที่หนังสือ
Pattern Languages of Program Design 2 ของ
Vlissides และ Coplien
จะมีบทหนึ่งที่เกี่ยวกับ Pattern ของการทำ
Reflection อ่านกันเต็มอิ่มเลยครับ (Vlissides
เป็นหนึ่งใน Gang of Four ที่เขียนหนังสือ
Design Pattern อันโด่งดังครับ)