ไปเขียนบทความฝั่ง Android ซะเยอะ วันนี้มาฝั่ง Visual Basic.NET กันบ้างละกันเน๊อะ
บทความนี้เกิดขึ้นมาจากความสงสัยของผม ว่า… ถ้าอยากเขียนคำสั่ง เพื่ออ่านชื่อ Property ใน Visual Basic.NET เพื่อนำไปใช้งานต่อ เช่น Print Log ออกมาดู จะต้องเขียนคำสั่งอย่างไร
ตัวอย่างเช่น Class Form จะมี Property FormBorderStyle อยู่ แล้วจะเขียนคำสั่งอย่างไร ให้ได้ออกมาเป็น String ว่า “FormBorderStyle”
เมื่อลองค้นไปเรื่อย ก็พบกับ Method GetCurrentMethod() ซึ่งเป็น Static Method ของ Class MethodBase โดยที่ Method GetCurrentMethod() นั้น Return เป็นชนิด Class MethodBase
ซึ่ง Class MethodBase นั้น Inherit มาจาก Class MemberInfo อีกที ซึ่งมี Property Name สำหรับอ่านชื่อออกมาได้…
อย่าเพิ่งงง… เพราะผมก็งง >_<
เอาเป็นว่า… มาลองทำกันเลยดีกว่า…
List of contents
- Get Property & Method name from Internal Class by MemberInfo
- Get Class & Namespace name from Internal Class by DeclaringType & ReflectedType
- Alternate way by StackFrame
- Get Property name from External Class
- Get Method & Class & Namespace name from External Class
- Alternate way by TypeName and GetType
Get Property & Method name from Internal Class by MemberInfo
จากที่เกริ่นด้านบน ก็เลยต้องมาลองดูกันหน่อย ในเมื่อ Method GetCurrentMethod() ได้ Return เป็น Class MethodBase ที่ Inherit มาจาก Class MemberInfo ซึ่งทั้งหลายทั้งปวง อยู่ใน Namespace System.Reflection
เพราะฉะนั้น เริ่มจาก สร้าง Class ขึ้นมาใหม่ แล้วทำการ Import System.Reflection ซะก่อน จะได้ไม่ต้องเขียน Code ยาว ๆ
1 |
Imports System.Reflection |
แล้วก็ทำการประกาศตัวแปรชนิด MemberInfo ไว้ภายใน Class
1 |
Private info As MemberInfo |
หลังจากนั้นก็ทดลองเรียกใช้งาน Method GetCurrentMethod() ตามจุดต่าง ๆ และทำ Function สำหรับ Print ค่าออกมาดูทาง Console ดังนี้
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
Imports System.Reflection Public Class MyVBClass Private info As MemberInfo Public Sub New() info = MethodBase.GetCurrentMethod() PrintConsole("Constructor") End Sub Public Property Property1() As String Get info = MethodBase.GetCurrentMethod() PrintConsole("Property [Get]") End Get Set(ByVal value As String) info = MethodBase.GetCurrentMethod() PrintConsole("Property [Set]") End Set End Property Public Sub Method1() info = MethodBase.GetCurrentMethod() PrintConsole("Method") End Sub Protected Overrides Sub Finalize() info = MethodBase.GetCurrentMethod() PrintConsole("Destructor") MyBase.Finalize() End Sub Private Sub PrintConsole(ByVal where As String) Console.WriteLine("GetCurrentMethod() at {0} - {1}", where.PadRight(15), TypeName(info)) End Sub End Class |
แล้วเขียนคำสั่งใช้งาน เพื่อดูผลลัพธ์ ดังนี้
1 2 3 |
Dim myObj As New MyVBClass() myObj.Property1 = myObj.Property1 myObj.Method1() |
1 2 3 4 5 |
GetCurrentMethod() at Constructor - RuntimeConstructorInfo GetCurrentMethod() at Property [Get] - RuntimeMethodInfo GetCurrentMethod() at Property [Set] - RuntimeMethodInfo GetCurrentMethod() at Method - RuntimeMethodInfo GetCurrentMethod() at Destructor - RuntimeMethodInfo |
สังเกตุว่า ที่ Constructor นั้น แท้จริงแล้ว Method GetCurrentMethod() ได้ Return เป็นชนิด RuntimeConstructorInfo ส่วน Property Get และ Set, Method หรือแม้แต่ Destructor ได้ Return เป็นชนิด RuntimeMethodInfo
ที่ Property Get และ Set ได้ผลลัพธ์เป็น RuntimeMethodInfo เพราะ Property ใน .NET มันก็คือ Get Method และ Set Method นั่นเอง
ทีนี้ ลองดูค่า Property Name ด้วยคำสั่งดังนี้
1 2 3 4 |
Private Sub PrintConsole(ByVal where As String) Console.WriteLine("GetCurrentMethod() at {0} - {1}", where.PadRight(15), TypeName(info)) Console.WriteLine(" .Name = {0}", info.Name) End Sub |
ได้ผลเป็นดังนี้
1 2 3 4 5 6 7 8 9 10 |
GetCurrentMethod() at Constructor - RuntimeConstructorInfo .Name = .ctor GetCurrentMethod() at Property [Get] - RuntimeMethodInfo .Name = get_Property1 GetCurrentMethod() at Property [Set] - RuntimeMethodInfo .Name = set_Property1 GetCurrentMethod() at Method - RuntimeMethodInfo .Name = Method1 GetCurrentMethod() at Destructor - RuntimeMethodInfo .Name = Finalize |
- ที่ Constructor ได้ค่าเป็น .ctor !? ก็เพราะ RuntimeConstructorInfo ได้ Fix ค่านี้ไว้
- ที่ Property Get และ Set จะได้ค่าเป็น get_<Property Name> และ set_<Property Name> ตามลำดับ
- ที่ Method และ Destructor จะได้ค่าเป็น <Method Name> และ Finalize ตามลำดับ
จะเห็นว่า เราอ่านชื่อ Property และ Method ได้แล้ว แต่ Property นี่ คงต้องตัดคำว่า get_ และ set_ ออกเอง!!
Get Class & Namespace name from Internal Class by DeclaringType & ReflectedType
ใน Class MemberInfo นั้น มี Property DeclaringType และ ReflectedType โดยทั้งสอง Property มี Return เป็นชนิด Class Type
ความแตกต่างของทั้ง 2 Property ดูได้ที่ Examples ของ ReflectedType นะครับ
ซึ่ง Class Type นั้น มี Property FullName, Name และ Namespace ให้เรียกใช้ ลองเขียนคำสั่งดูค่าจากทั้ง 3 Property ดู ดังนี้
1 2 3 4 5 6 |
Private Sub PrintConsole(ByVal type As Type) Console.WriteLine(" .ReflectedType") Console.WriteLine(" .FullName = {0}", type.FullName) Console.WriteLine(" .Name = {0}", type.Name) Console.WriteLine(" .Namespace = {0}", type.Namespace) End Sub |
แล้วเรียกใช้ Function นี้ ที่ไหนก็ได้ใน Class เช่น เรียกใช้ที่ Constructor ดังนี้
1 2 3 4 5 |
Public Sub New() info = MethodBase.GetCurrentMethod() PrintConsole("Constructor") PrintConsole(info.ReflectedType) End Sub |
จะได้ผลลัพธ์เป็น…
1 2 3 4 5 6 |
GetCurrentMethod() at Constructor - RuntimeConstructorInfo .Name = .ctor .ReflectedType .FullName = Test_GetCurrentMethod.MyVBClass .Name = MyVBClass .Namespace = Test_GetCurrentMethod |
- Property FullName จะได้ค่าเป็น <Namespace>.<Class Name>
- Property Name จะได้ค่าเป็น <Class Name>
- Property Namespace จะได้ค่าเป็น <Namespace>
จะเห็นว่า เราอ่านชื่อ Class และ Namespace ได้แล้ว!!
Alternate way by StackFrame
ยังมี Method GetMethod() ของ Class StackFrame ที่มี Return เป็นชนิด Class MethodBase ได้เช่นกัน เพียงแต่ว่า เราต้องสร้าง Instant จาก Class StackFrame ขึ้นมาก่อน
ซึ่งคราวนี้ ผมขอเพิ่ม Method ใหม่สำหรับ Class MyVBClass เพื่อทดสอบ ดังนี้
1 2 3 4 5 6 7 8 9 |
Public Function Method2(ByVal param1 As Integer) As String Dim stackFrame As New System.Diagnostics.StackFrame() info = stackFrame.GetMethod() PrintConsole("Method2") PrintConsole(info.ReflectedType) Return "Return value from Method2" End Function |
1 2 3 4 5 6 |
GetCurrentMethod() at Method2 - RuntimeMethodInfo .Name = Method2 .ReflectedType .FullName = Test_GetCurrentMethod.MyVBClass .Name = MyVBClass .Namespace = Test_GetCurrentMethod |
ซึ่งผลลัพธ์ที่ได้ ก็เหมือนกับการใช้ Method GetCurrentMethod() ทุกประการ
และนอกจากเขียนคำสั่งอยู่ภายใน Method แล้ว ใน Constructor, Destructor หรือ Property Get และ Set ก็สามารถใช้งานได้เช่นกัน
รวม ๆ Code ทั้งหมด สำหรับ Class MyVBClass ก็จะได้เป็น
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
Imports System.Reflection Public Class MyVBClass Private info As MemberInfo Public Sub New() info = MethodBase.GetCurrentMethod() PrintConsole("Constructor") PrintConsole(info.ReflectedType) End Sub Public Property Property1() As String Get info = MethodBase.GetCurrentMethod() PrintConsole("Property [Get]") PrintConsole(info.ReflectedType) End Get Set(ByVal value As String) info = MethodBase.GetCurrentMethod() PrintConsole("Property [Set]") PrintConsole(info.ReflectedType) End Set End Property Public Sub Method1() info = MethodBase.GetCurrentMethod() PrintConsole("Method") PrintConsole(info.ReflectedType) End Sub Public Function Method2(ByVal param1 As Integer) As String Dim stackFrame As New System.Diagnostics.StackFrame() info = stackFrame.GetMethod() PrintConsole("Method2") PrintConsole(info.ReflectedType) Return "Return value from Method2" End Function Protected Overrides Sub Finalize() info = MethodBase.GetCurrentMethod() PrintConsole("Destructor") PrintConsole(info.ReflectedType) MyBase.Finalize() End Sub Private Overloads Sub PrintConsole(ByVal where As String) Console.WriteLine("GetCurrentMethod() at {0} - {1}", where.PadRight(15), TypeName(info)) Console.WriteLine(" .Name = {0}", info.Name) End Sub Private Overloads Sub PrintConsole(ByVal type As Type) Console.WriteLine(" .ReflectedType") Console.WriteLine(" .FullName = {0}", type.FullName) Console.WriteLine(" .Name = {0}", type.Name) Console.WriteLine(" .Namespace = {0}", type.Namespace) End Sub End Class |
เผื่อใครอยาก Copy ไปรันเล่นดู
พักเหนื่อย 10 นาที… >_<
จากที่ผ่านมาด้านบน เป็นการเขียนคำสั่งอ่านชื่อของ Property, Method, Class และ Namespace โดยเขียนคำสั่งอยู่ภายใน Class ที่เราสร้างขึ้นมาเอง
แต่หากว่า อยากเขียนคำสั่งเพื่ออ่านชื่อต่าง ๆ จาก Class อื่น ๆ ที่เราไม่ได้สร้างขึ้นมาเอง เหมือนโจทย์ที่ตั้งไว้ในต้นบทความ ที่ว่า…
ตัวอย่างเช่น Class Form จะมี Property FormBorderStyle อยู่ แล้วจะเขียนคำสั่งอย่างไร ให้ได้ออกมาเป็น String ว่า “FormBorderStyle”
Get Property name from External Class
สำหรับ Property เราต้องสร้าง Function ขึ้นมา โดยใช้หลักการของ Lambda Expressions และ Delegate (ซึ่งเป็นเรื่องที่เข้าใจยากใน VB.NET ซึ่งไม่ขออธิบายในบทความนี้นะครับ ถ้าอยากรู้ คลิกเข้าไปดูใน Reference ที่ผมทำ Link ไว้ให้นะ) ดังนี้
1 2 3 4 5 |
Private Function GetPropertyName(Of T)(ByVal expression As Expressions.Expression(Of Func(Of T))) As String Dim memberExpression As Expressions.MemberExpression = DirectCast(expression.Body, Expressions.MemberExpression) Return memberExpression.Member.Name End Function |
เวลาใช้งานก็…
1 2 3 4 5 |
Dim myObj As MyVBClass = Nothing Dim strName As String = GetPropertyName(Function() myObj.Property1) Console.WriteLine("Property name = " & strName) |
1 |
Property name = Property1 |
อันที่จริง Function นี้ สามารถใช้ภายใน Class ของเราเองได้ด้วย แต่ต้องนำหน้า Property ด้วย Me เช่น Me.Property1
ถ้าอยากได้ “FormBorderStyle” ของ Class Form ก็แค่…
1 2 3 4 5 |
Dim myForm As Form = Nothing Dim strName As String = GetPropertyName(Function() myForm.FormBorderStyle) Console.WriteLine("Property name = " & strName) |
1 |
Property name = FormBorderStyle |
สังเกตุว่า ทั้ง 2 ตัวอย่าง ทั้งตัวแปร myObj และ myForm ไม่จำเป็นต้องสร้าง Instant ก็ได้ แค่ระบุ Type แล้วนำไปใช้งานได้เลย
Get Method & Class & Namespace name from External Class
สำหรับ Method เราสามารถใช้งาน Class MemberInfo ได้เช่นเดียวกับที่เราเขียนภายใน Class แต่จะต้องใช้ Delegate มาช่วยนิดหน่อย…
โดยเริ่มจากการ Import System.Reflection ซะก่อน จะได้ไม่ต้องเขียน Code ยาว ๆ
1 |
Imports System.Reflection |
แล้วก็ทำการประกาศตัวแปรชนิด MemberInfo ไว้ภายใน Class
1 |
Private info As MemberInfo |
หลังจากนั้น เราจะต้องสร้าง Instant จาก Class ที่จะอ่านชื่อก่อน แล้วใช้ Delegate Sub/Function ในการ Reference ไปยัง Method ที่ต้องการ ซึ่ง Delegate Sub/Function จะมี Property Method ที่ Return เป็นชนิด MethodInfo เพื่อใช้อ่านชื่อ Method, Class และ Namespace ต่อไป ดังนี้
1 2 3 4 5 6 7 8 9 10 11 12 13 |
Dim myObj As New MyVBClass() Dim method1 As Action = AddressOf myObj.Method1 info = method1.Method PrintConsole("Method1") PrintConsole(info.ReflectedType) Dim method2 As Func(Of Integer, String) = AddressOf myObj.Method2 info = method2.Method PrintConsole("Method2") PrintConsole(info.ReflectedType) |
1 2 3 4 5 6 7 8 9 10 11 12 |
GetCurrentMethod() at Method1 - RuntimeMethodInfo .Name = Method1 .ReflectedType .FullName = Test_GetCurrentMethod.MyVBClass .Name = MyVBClass .Namespace = Test_GetCurrentMethod GetCurrentMethod() at Method2 - RuntimeMethodInfo .Name = Method2 .ReflectedType .FullName = Test_GetCurrentMethod.MyVBClass .Name = MyVBClass .Namespace = Test_GetCurrentMethod |
จุดที่ยากก็คือ เราต้องสร้าง Delegate Sub/Function ให้มีชนิด Parameter และ Return type ตรงกับ Method เป้าหมาย ดังใน Code ด้านบน คือบรรทัดที่ 3 และ 9 นั่นเอง
อย่าลืม Copy function PrintConsole มาใส่ด้วยนะ Code ด้านบนยืมมาใช้ ^_^
Alternate way by TypeName and GetType
ยังมีวิธีอ่านชื่อ Class ง่าย ๆ ด้วย Function TypeName ซึ่งจะ Return ออกมาเป็น String ให้ได้เลย ดังนี้
1 |
Console.WriteLine("Class name = " & TypeName(myObj)) |
1 |
Class name = MyVBClass |
หรือจะใช้ Operator GetType ซึ่งจะ Return ออกมาเป็น Class Type ได้ ดังนี้
1 2 |
Dim myType As Type = GetType(MyVBClass) PrintConsole(myType) |
1 2 3 4 |
.ReflectedType .FullName = Test_GetCurrentMethod.MyVBClass .Name = MyVBClass .Namespace = Test_GetCurrentMethod |
จบละครับ สำหรับการอ่านชื่อของ Property, Method, Class และ Namespace บน Visual Basic.NET ฟู่วววววววววว~!!
เหมือนจะยาก จริง ๆ ไม่ได้ยาก แต่ยุ่งยากมากกว่า ว่ามะ >_<