Introduction
This is my first article on The Code Project. I will keep it brief and to the point.
The purpose of this application is to utilize .NET Reflection
to create a console application that will load any .NET assembly, query all of its classes and objects, construct instances of these objects, and invoke its methods and properties. The current version still has a lot of bugs, but I will fix these bugs in the near future. But this gives you an idea of what I am trying to accomplish.
Background
The example I've provided in this article uses an Assembly
called MessageHandler
which is included in the solution.
To run the example, simply run the ReflectionText.exe "assembly path
".
Using the Code
First we reference the Reflection Assembly
...
Imports System.Reflection
To load an assembly, we simply give the path of the assembly
file.
msgHan = Assembly.LoadFile(assemblyFilePath)
To get a list of all the Type
s, we will need to call the GetTypes
method of that Type
object.
Dim types As Type() = msgHan.GetTypes()
A Type
object is a blue print of the signature of a specific Type
. It holds information about its Methods, Properties, Variables, etc. It also contains meta information and constructor information.
To Get a list of constructors of an instance class, we call the GetConstructor
function.
A constructor is simply a method that is invoked when an instance of a class is created. It can be overloaded to create difference initial behaviors of that instance by passing in different parameters.
Dim cons As ConstructorInfo() = typMsg.GetConstructors()
To show a list of parameters of a method, I've included this function in my code:
Private Function GetMethodSignature(ByVal method As MethodBase) As String
If method Is Nothing Then Return Nothing
Dim parms As ParameterInfo() = method.GetParameters()
If parms Is Nothing OrElse parms.Length = 0 Then Return "()"
Const COMMA As String = ", ", strAS = " as "
Dim sb As New Text.StringBuilder(parms.Length * 50)
sb.Append("(")
For i As Integer = 0 To parms.Length - 1
sb.Append(parms(i).Name)
sb.Append(strAS)
sb.Append(parms(i).ParameterType.Name)
sb.Append(COMMA)
Next
If sb.Length > 1 Then sb.Remove(sb.Length - COMMA.Length, COMMA.Length)
sb.Append(")")
Return sb.ToString()
End Function
The Object ParameterInfo
contains information about a parameters Value Type, meta data, default values etc.
In the above code, I used a string builder to list out all the parameters of a method and its type in the way the function is written.
Once the parameters info is displayed, we can simply create an Array of Object Type that will hold the user input for a particular parameter, and convert it to the type of the parameter by using Convert.ChangeType
. If the types does not have a converter, an exception will be thrown.
Private Function GetParamValues(ByVal parms As ParameterInfo()) As Object()
Dim args(parms.Length - 1) As Object
Console.WriteLine()
Console.WriteLine(" ----- Parameters ----- ")
For i As Integer = 0 To parms.Length - 1
Dim parmType As Type = parms(i).ParameterType
Console.WriteLine(parms(i).Name & ":")
args(i) = Convert.ChangeType(Console.ReadLine(), parmType)
Next
Console.WriteLine(" ------------------- ")
Return args
End Function
Once we have collected the Parameter Values, we can simply invoke that particular method by using the Method.Invoke
function which will execute that method on the same thread and return a value of Type
Object.
In the example project I provided, I also included the number of times you want to invoke the method using Reflection for Unit Testing Purposes.
ret = meth.Invoke(clsMsg, args)
And we are done.
I've included a function to display the information retrieved from the function with a recursive algorithm. Since a return value can be a complex data type, we can iterate through the return type, invoke the get
property of all its properties, and have a better understanding.
Private Function GetClassFields(ByVal obj As Object) As String
If obj Is Nothing Then Return String.Empty
Dim sb As New Text.StringBuilder(256 * 500)
Return GetClassProperties(obj, sb, 0)
End Function
Private Function GetClassProperties(ByVal obj As Object, _
ByVal Sb As Text.StringBuilder, ByVal Depth As Integer) As String
Dim objType As Type = obj.GetType()
Sb.Append(Space(TAB_LENGTH * Depth))
Sb.Append(".")
Sb.Append(objType.Name)
Sb.Append(vbNewLine)
Try
Dim props As PropertyInfo()
props = objType.GetProperties()
For i As Integer = 0 To props.Length - 1
If props(i).CanRead Then
Dim getmeth As MethodInfo = props(i).GetGetMethod()
Dim ret As Object = Nothing
If getmeth.GetParameters().Length = 0 Then
ret = getmeth.Invoke(obj, Nothing)
End If
If Not Depth < 10 AndAlso ret IsNot Nothing AndAlso _
Not ret.GetType().IsValueType AndAlso Not TypeOf (ret) _
Is String Then
Sb.Append(GetClassProperties(ret, Sb, Depth + 1))
Else
Sb.Append(Space(TAB_LENGTH * Depth + 1))
Sb.Append(".")
Sb.Append(props(i).Name)
Sb.Append(":")
If ret Is Nothing Then
Sb.Append(vbNewLine)
If TypeOf (obj) Is IEnumerable Then
For Each o As Object In obj
Sb.Append(GetClassProperties(o, Sb, Depth + 1))
Next
End If
Else
Sb.Append(ret.ToString())
End If
Sb.Append(vbNewLine)
End If
End If
Next
Return Sb.ToString()
Catch ex As Exception
Return Sb.ToString()
End Try
End Function
Private Function GetClassFields(ByVal obj As Object, _
ByVal Sb As Text.StringBuilder, ByVal Depth As Integer) As String
Dim objType As Type = obj.GetType()
Sb.Append(Space(TAB_LENGTH * Depth))
Sb.Append(".")
Sb.Append(objType.Name)
Sb.Append(vbNewLine)
Try
Dim fields As FieldInfo() = objType.GetFields()
For i As Integer = 0 To fields.Length - 1
If fields(i).IsPublic Then
Dim ret As Object = fields(i).GetValue(obj)
If Not Depth < 10 AndAlso Not ret.GetType().IsValueType _
AndAlso Not TypeOf (ret) Is String Then
Sb.Append(GetClassFields(ret, Sb, Depth + 1))
Else
Sb.Append(Space(TAB_LENGTH * Depth + 1))
Sb.Append(".")
Sb.Append(fields(i).Name)
Sb.Append(":")
Sb.Append(ret.ToString())
Sb.Append(vbNewLine)
End If
End If
Next
Return Sb.ToString()
Catch ex As Exception
Return Sb.ToString()
End Try
End Function
Points of Interest
Reflection can be very useful in many ways. You can use this as a base of a pluggable application.
This particular application can be used in a remote shell, or a BAT file to execute a particular process. It is kind of like the Windows Power Shell.
History
- 18th October, 2007 - Draft of article posted