The code then adds a node labeled Referenced Assembliesto the tree view.. The code adds the assembly’s entry point to the tree view, and then loops through the exported publictypes defin
Trang 1Even if you don’t expect your program to interact with other assemblies, you may find somereflection techniques useful For example, if your application uses only its own modules, you maynot think you need to use reflection to learn about what is contained in those modules After all,you are writing the code, so you should know what it contains
However, it may still be useful for the program to load assemblies at run-time For example, theprogram can look in a tools directory to see what assemblies are available and load any tools thatthey define Then, if you later decide to add, modify, or remove a tool, you only need to add,replace, or remove an assembly from this directory, and the program will automatically updateitself the next time it runs That makes distributing new tools to users quick and easy
There isn’t enough room in this chapter to cover all of the reflection tools provided by the NETFramework, but there is enough room to cover some of the most useful of those tools That shouldgive you enough of a start that you can figure out how to use other tools as needed
Exploring Assemblies
An obvious first task when using reflection is learning information about an assembly
An assembly is the smallest unit of deployment in a NET application It is basically a piece of compiled code Generally, that means a compiled .exeor .dllfile.
Trang 2Example program GetAssemblyInformation(available for download at www.vb-helper.com/one_on_one.htm) displays information about an assembly stored in a file It’s a long and involved program,but its basic operations are relatively simple when taken individually The program doesn’t even try todisplay all of the possible information about the assembly Instead, it sticks to the most common anduseful bits of information.
When you enter the path to an assembly and click the Go button, the program executes the followingcode:
‘ Display information about the assembly
Private Sub btnGo_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnGo.Click
Dim assem As [Assembly] = [Assembly].LoadFrom(txtPath.Text)
‘ Display the assembly’s full name
trvDetails.Nodes.Clear()trvDetails.Nodes.Add(“Full Name: “ & assem.FullName)trvDetails.Nodes.Add(“Location: “ & assem.Location)
‘ Display referenced assemblies
Dim ref_assem_node As TreeNode = trvDetails.Nodes.Add(“Referenced Assemblies”)For Each referenced_assembly As AssemblyName In assem.GetReferencedAssemblies()ref_assem_node.Nodes.Add(referenced_assembly.FullName)
Next referenced_assembly
‘ Display the assembly’s entry point
Dim entry_point As MethodInfo = assem.EntryPoint()trvDetails.Nodes.Add(“Entry Point: “ & entry_point.Name)
‘ Types
Dim types_node As TreeNode = trvDetails.Nodes.Add(“Types”)For Each exported_type As Type In assem.GetTypes()
AddTypeData(types_node, exported_type)Next exported_type
End Sub
First, the code creates an Assemblyobject based on the assembly file you entered in the txtPathtextbox It then clears the trvDetails TreeViewcontrol, where it will store its results, and adds the assem-bly’s full name (name, version, culture, and token) and its location to the tree view
The code then adds a node labeled Referenced Assembliesto the tree view It loops through the ues in the assemblies returned by the Assemblyobject’s GetReferencedAssembliesfunction, addingeach assembly’s name to the tree view
val-This code shows a common operation when exploring an assembly: the code finds an object and loops
through an array representing items that are logically contained in the original object In this case, the
Assemblyobject provides a list of referenced assemblies Similarly, a Typeobject representing a class can have properties, fields, methods, events, and nested types.
The code adds the assembly’s entry point to the tree view, and then loops through the exported (public)types defined by the assembly For each of these types, the code calls subroutine AddTypeDatato addinformation about the type to the tree view The following code shows subroutine AddTypeData:
Trang 3‘ Return information about a type.
Private Sub AddTypeData(ByVal parent_node As TreeNode, ByVal the_type As Type)Dim txt As String = “”
If the_type.IsPublic Then txt &= “Public “
If the_type.IsInterface Then
‘ Skip MustInherit for Interfaces
txt &= “Interface “Else
If the_type.IsAbstract Then txt &= “MustInherit “End If
If the_type.IsClass Then txt &= “Class “
If the_type.IsEnum Then txt &= “Enum “
If the_type.IsValueType Then txt &= “Structure “
Dim type_name As String = the_type.Name
If the_type.ContainsGenericParameters Then
‘ Remove whatever comes after `
type_name = type_name.Substring(0, type_name.IndexOf(“`”))
‘ Compose the generic parameters
Dim generic_params As String = “”
Dim generic_type As Type = the_type.GetGenericTypeDefinitionFor Each arg_type As Type In generic_type.GetGenericArguments()generic_params &= “, “ & arg_type.Name
Next arg_type
If generic_params.Length > 0 Then _generic_params = generic_params.Substring(2)type_name &= “(Of “ & generic_params & “)”
End IfDim type_node As TreeNode = parent_node.Nodes.Add(txt & type_name)
If the_type.BaseType Is Nothing Thentype_node.Nodes.Add(“Inherits From: Nothing”)Else
type_node.Nodes.Add(“Inherits From: “ & the_type.BaseType.FullName)End If
type_node.Nodes.Add(“Attributes: “ & the_type.Attributes.ToString)type_node.Nodes.Add(“Qualified Name: “ & the_type.AssemblyQualifiedName)
‘ See if it’s an enum
If the_type.IsEnum Then
‘ It’s an enum List its values
Dim values_node As TreeNode = type_node.Nodes.Add(“Values”)For Each field_info As FieldInfo In the_type.GetFields()
If field_info.IsStatic Thenvalues_node.Nodes.Add(field_info.Name & “ = “ & _CLng(field_info.GetValue(Nothing)))
Elsevalues_node.Nodes.Add(field_info.Name & “ (non static)”)End If
Next field_infoElse
‘ It’s not an enum
‘ Interfaces
Dim ifaces() As Type = the_type.GetInterfaces()
Trang 4If ifaces.Length > 0 ThenDim ifaces_node As TreeNode = type_node.Nodes.Add(“Interfaces”)For Each iface_type As Type In ifaces
AddInterfaceData(ifaces_node, iface_type)Next iface_type
End If
‘ Constructors
Dim constructor_infos() As ConstructorInfo = the_type.GetConstructors()
‘ If constructor_infos = Nothing,
‘ the type has only a default constructor
‘ If constructor_infos is empty, it has no constructors
If constructor_infos Is Nothing Then
‘ It has no explicit constructors,
‘ only the default empty constructor
Dim constr_node As TreeNode = type_node.Nodes.Add(“Constructors”)constr_node.Nodes.Add(“New “ & type_name & “()”)
ElseIf constructor_infos.Length > 0 ThenDim constr_node As TreeNode = type_node.Nodes.Add(“Constructors”)For Each constructor_info As ConstructorInfo In constructor_infosAddConstructorData(constr_node, type_name, constructor_info)Next constructor_info
End If
‘ Fields
Dim field_infos() As FieldInfo = the_type.GetFields()
If field_infos.Length > 0 ThenDim fields_node As TreeNode = type_node.Nodes.Add(“Fields”)For Each field_info As FieldInfo In field_infos
AddFieldData(fields_node, field_info)Next field_info
End If
‘ Properties
Dim property_infos() As PropertyInfo = the_type.GetProperties()
If property_infos.Length > 0 ThenDim properties_node As TreeNode = type_node.Nodes.Add(“Properties”)For Each property_info As PropertyInfo In property_infos
AddPropertyData(properties_node, property_info)Next property_info
End If
‘ Methods
Dim method_infos() As MethodInfo = the_type.GetMethods()
If method_infos.Length > 0 ThenDim methods_node As TreeNode = type_node.Nodes.Add(“Methods”)For Each method_info As MethodInfo In method_infos
AddMethodData(methods_node, method_info)Next method_info
End If
‘ Events
Dim event_infos() As EventInfo = the_type.GetEvents()
If event_infos.Length > 0 Then
Trang 5Dim events_node As TreeNode = type_node.Nodes.Add(“Events”)For Each event_info As EventInfo In event_infos
AddEventData(events_node, event_info)Next event_info
End If
‘ Nested types
Dim nested_types() As Type = the_type.GetNestedTypes()
If nested_types.Length > 0 ThenDim nested_types_node As TreeNode = type_node.Nodes.Add(“Nested Types”)For Each nested_type As Type In nested_types
AddTypeData(nested_types_node, nested_type)Next nested_type
End IfEnd IfEnd Sub
Subroutine AddTypeDatatakes a Typeobject as a parameter It checks the Type’s properties to mine whether the type is public, an interface, MustInherit, a class, an enumerated type, or a structure
deter-It uses this information to build an appropriate declaration string for the type
Next, the routine checks whether the type contains generic parameters, as in Public ClassTestClass(Of T1, T2) If it does, the code loops through the generic arguments to build an appropri-ate parameter list
The code then adds a node to the tree view containing the type’s basic declaration The code adds anyother tree view nodes for this type as descendants of this node
The subroutine adds entries, giving the type from which this one inherits (note that interfaces don’tinherit from anything), its attributes, and its fully qualified name It then considers other items that arelogically contained within the type
If the type is an Enum, the code loops through its fields If a field is static (has a constant value), the codedisplays its name and its value If a field is not static, the code displays the field’s name
If the type is not an Enum, the subroutine loops through and displays information about the interfaces itimplements It then describes the type’s constructors, fields, properties, methods, events, and nestedtypes (for example, classes defined within this class)
For each of these more-complicated items, the AddTypeDatasubroutine calls other routines to provideadditional information For example, for a constructor, it calls subroutine AddConstructorData For anested type, subroutine AddTypeDatacalls itself recursively to describe the nested type
The other routines used by the program are much simpler Subroutine AddConstructorDatashown inthe following code adds a node to the tree view describing a constructor It starts with the keyword New
and uses function ParameterListto list its parameters
‘ Return information about a type
Private Sub AddConstructorData(ByVal parent_node As TreeNode, _ByVal type_name As String, ByVal constructor_info As ConstructorInfo)
‘ Get the constructor’s parameters
Dim txt As String = “New “ & type_name & _
Trang 6End Sub
Function ParameterListloops through an array of ParameterInfoobjects and calls GetParameterDatato get a declaration for each
‘ Return a string listing parameters from a collection
Private Function ParameterList(ByVal param_infos() As ParameterInfo) As StringDim params As String = “”
For Each parameter_info As ParameterInfo In param_infosparams &= “, “ & GetParameterData(parameter_info)Next parameter_info
If params.Length > 0 Then params = params.Substring(2)
Return “(“ & params & “)”
End Function
Function GetParameterDataexamines a ParameterInfoobject and builds its declaration as it wouldappear within a parameter list This function is somewhat limited For example, it doesn’t try to com-pletely interpret parameters that are of generic types as in Sub Test(ByVal values As List(OfBoolean))
‘ Return information about a parameter
Private Function GetParameterData(ByVal parameter_info As ParameterInfo) As StringDim txt As String = “”
If parameter_info.IsOptional Thentxt &= “Optional “
End If
If parameter_info.IsOut Thentxt &= “ByRef “
Elsetxt &= “ByVal “End If
txt &= parameter_info.Name
If parameter_info.ParameterType.IsArray Thentxt &= “()”
txt &= “ As “ & parameter_info.ParameterType.GetElementType.NameElse
txt &= “ As “ & parameter_info.ParameterType.NameEnd If
End If
Return txtEnd Function
Trang 7Subroutines AddFieldDataand AddPropertyData, shown in the following code, examine FieldInfo
and PropertyInfoobjects to provide reasonable declarations for fields (public variables) and properties
‘ Add information about a field
Private Sub AddFieldData(ByVal fields_node As TreeNode, _ByVal field_info As FieldInfo)
Dim txt As String = “”
If field_info.IsPublic Then txt &= “Public “
If field_info.IsPrivate Then txt &= “Private “
If field_info.IsFamily Then txt &= “Protected “
If field_info.IsStatic Then txt &= “Shared “txt &= field_info.Name
txt &= “ As “ & field_info.FieldType.Namefields_node.Nodes.Add(txt)
End Sub
‘ Add information about a property
Private Sub AddPropertyData(ByVal properties_node As TreeNode, _ByVal property_info As PropertyInfo)
Dim txt As String = “Public “
If property_info.CanRead AndAlso Not property_info.CanWrite Thentxt &= “ReadOnly “
ElseIf Not property_info.CanRead AndAlso property_info.CanWrite Thentxt &= “WriteOnly “
End Iftxt &= property_info.Nametxt &= “ As “ & property_info.PropertyType.Nameproperties_node.Nodes.Add(txt)
‘ Add information about a method
Private Sub AddMethodData(ByVal methods_node As TreeNode, _ByVal method_info As MethodInfo)
methods_node.Nodes.Add(MethodSignature(method_info))End Sub
‘ Return a method’s signature in a VB format
Private Function MethodSignature(ByVal method_info As MethodInfo) As StringDim txt As String = “”
If method_info.IsFamilyAndAssembly Thentxt &= “Protected Friend “
ElseIf method_info.IsFamily Thentxt &= “Protected “
ElseIf method_info.IsFamilyOrAssembly Thentxt &= “Friend “
ElseIf method_info.IsPrivate Thentxt &= “Private “
ElseIf method_info.IsPublic Then
Trang 8txt &= “Public “End If
If method_info.IsFinal Then txt &= “NotOverridable “
If method_info.IsHideBySig Then txt &= “Overrides “
If method_info.IsAbstract Then txt &= “MustOverride “
If method_info.IsStatic Then txt &= “Shared “
If method_info.ReturnType.Name = “Void” Thentxt &= “Sub “
Elsetxt &= “Function “End If
txt &= method_info.Nametxt &= ParameterList(method_info.GetParameters())
If method_info.ReturnType.Name <> “Void” Thentxt &= “ As “ & method_info.ReturnType.NameEnd If
Return txtEnd Function
Subroutine AddEventDatasimply adds an event’s name to the tree view, and subroutine
AddInterfaceDatajust adds the keyword Implementsand the interface’s name to the output
‘ Add information about an event
Private Sub AddEventData(ByVal events_node As TreeNode, _
ByVal event_info As EventInfo)
events_node.Nodes.Add(event_info.Name)End Sub
‘ Add information about an implemented interface
Private Sub AddInterfaceData(ByVal ifaces_node As TreeNode, _
ByVal iface_type As Type)
ifaces_node.Nodes.Add(“Implements “ & iface_type.Name)End Sub
Figure 22-1 shows program GetAssemblyInformationin action In this figure, the Employeeclass isopen and displaying its interfaces, constructors, fields, and properties
You can download the example program at www.vb-helper.com/one_on_one.htmand look at the
Employeeclass it defines to see how it leads to the results shown in Figure 22-1
Example program GetMyAssemblyInformationis almost the same as program
GetAssemblyInformation, except it doesn’t load an Assemblyobject from a file Instead this programuses the following statement to get an Assemblyrepresenting itself as it is running:
Dim assem As [Assembly] = [Assembly].GetExecutingAssembly()
This is reflection in a fairly literal sense The code is actually examining at itself as if it were looking in
a mirror.
Trang 9Figure 22-1: Program GetAssemblyInformationdisplays information about anassembly.
You may find program GetAssemblyInformationuseful for exploring assemblies, but it’s more important
as an example showing how you can search through an assembly for particular information For example, itshows how you can search an assembly to find the classes it defines It also shows how to examine thoseclasses to see which ones provide default constructors that take no parameters Your program can easilymake instances of those classes and possibly use their methods Example programs UseReflectionTools
and UseDiscoveryLib(described later in this chapter) demonstrate this technique
Exploring EnumerationsThe NET Framework includes many enumerations, including some that are very large such as
HatchStylewith 56 entries and Colorwith 141 entries Suppose you are building a drawing programand you want to let the user pick from among these values
Building the lists of HatchStylesand Colorswould be a lot of work It would also make maintenancemore difficult If a later release of the NET Framework defined new colors, for example, you wouldhave to figure out which ones were new and add them to your list
An alternative solution is to use reflection to make the HatchStyleand Colortypes list their own values.Example program ShowHatchBrushes(available for download at www.vb-helper.com/one_on_one.htm) uses the following code to show samples of the defined HatchStylevalues:
‘ Display brush samples
Private Sub Form1_Paint(ByVal sender As Object, _
Trang 10ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
e.Graphics.Clear(Me.BackColor)Dim x As Integer = XMINDim y As Integer = XMIN
‘ Enumerate the HatchBrush styles
Dim hatch_style_type As Type = GetType(HatchStyle)
Dim field_infos() As FieldInfo = hatch_style_type.GetFields()For Each field_info As FieldInfo In field_infos
‘ See if this is a static (Shared) property
‘ We only want the Shared properties such as Horizontal
‘ and not the instance properties such as value
If field_info.IsStatic Then
‘ Get this brush
Dim style_obj As Object = field_info.GetValue(Nothing)Dim hatch_style As HatchStyle = DirectCast(style_obj, HatchStyle)
‘ Make the brush and draw the sample
Using br As New HatchBrush(hatch_style, Color.Black, Color.White)DrawSample(e.Graphics, x, y, br, field_info.Name)
End UsingEnd IfNext field_infoEnd Sub
‘ Draw the sample and move (x, y) to a new position
Private Sub DrawSample(ByVal gr As Graphics, ByRef x As Integer, _
ByRef y As Integer, ByVal br As Brush, ByVal brush_name As String)
‘ Draw the sample
gr.FillRectangle(br, x, y, SAMPLE_WID, SAMPLE_HGT)gr.DrawRectangle(Pens.Red, x, y, SAMPLE_WID, SAMPLE_HGT)
‘ Draw the brush’s name
Dim string_format As New StringFormat()string_format.Alignment = StringAlignment.Centerstring_format.LineAlignment = StringAlignment.Neargr.DrawString(brush_name, Me.Font, Brushes.Blue, _
x + SAMPLE_WID \ 2, y + SAMPLE_HGT, string_format)
End Sub
The form’s Paintevent handler gets a Typeobject representing the HatchStyletype It then loopsthrough the FieldInfoobjects representing the type’s fields It uses each FieldInfo’s IsStatic
method to determine whether a field is a constant, and calls subroutine DrawSampleto draw a sample
of the static fields
Trang 11Subroutine DrawSamplefills a rectangle with the sample brush, draws a rectangle around it, and plays the style’s name underneath It then updates the X and Y coordinates where it will draw the nextsample.
dis-Figure 22-2 shows program ShowHatchBrushesdisplaying the hatch styles defined by the HatchStyle
‘ Enumerate the SystemColors styles
Dim system_colors_type As Type = GetType(SystemColors)Dim property_infos() As PropertyInfo = system_colors_type.GetProperties()For Each property_info As PropertyInfo In property_infos
‘ Get this color
Dim color_obj As Object = property_info.GetValue(Nothing, Nothing)Dim system_color As Color = DirectCast(color_obj, Color)
‘ Save the color and name
Trang 12m_Colors.Add(system_color)m_ColorNames.Add(property_info.Name)Next property_info
‘ Prepare for Paint events
Me.SetStyle( _ControlStyles.AllPaintingInWmPaint Or _ControlStyles.ResizeRedraw Or _
ControlStyles.OptimizedDoubleBuffer, _True)
Me.UpdateStyles()
Me.ClientSize = New Size( _
2 * XMIN + 6 * SAMPLE_WID + 5 * X_MARGIN, _
3 * XMIN + 6 * SAMPLE_HGT + 5 * Y_MARGIN)End Sub
The form’s Paintevent handler draws samples of the colors in the m_Colorsarray Download the code
to see the details
Figure 22-3 shows program ShowSystemColorsin action
Figure 22-3: Program ShowSystemColorsdraws samples of the values defined by
Trang 13Program Enumeration
ShowSystemColors SystemColorsShowSystemIcons SystemIcons
Loading Classes DynamicallyChapter 9, “Scripting,” explains some ways you can add new functionality to an application In particu-lar it explains how you can make an application compile and execute new Visual Basic code at run-time.Using this technique, you can add new features to a previously compiled application
Chapter 18, “Deployment,” discusses different methods that you can use to deploy an application If youdon’t change a method’s signature, you may be able to change its behavior to provide new features for
an existing application For example, suppose you build a class library that defines a Toolsclass Thisclass provides an InstallToolsmethod that adds new items to the main program’s menus and han-dles the new items’ Clickevents If you are careful, you can change the way InstallToolsworks andcopy a new DLL into the program’s directory without breaking the application
Reflection gives you another option for loading and executing new code Instead of compiling new code
at run-time or relying on the classes in a DLL not changing in significant ways, a program can use tion to open an assembly, find useful classes in it, and use them
reflec-The ReflectionToolsexample project (available for download at www.vb-helper.com/one_on_one.htm) builds a DLL that contains classes that provide tools for another application The followingcode shows how the library installs tools:
Imports System.Windows.FormsImports System.Drawing
‘ Identifies the tool installer class
Public Interface IToolInstallerSub InstallTools(ByVal tool_menu As ToolStripMenuItem, _ByVal rch As RichTextBox)
End Interface
‘ Lists the available tool classes
Public Class ToolInstallerImplements IToolInstaller
‘ Install tools for this application
Public Sub InstallTools(ByVal tool_menu As ToolStripMenuItem, _ByVal rch As System.Windows.Forms.RichTextBox) _
Implements IToolInstaller.InstallToolsDim bold_toggler As New BoldStyleTogglerbold_toggler.InstallTool(tool_menu, rch)End Sub
End Class