Reflection enables retrieving information on
programs, including modules, types, and type members defined within an
assembly. For example you might want to enumerate all types and type
members defined in the People.dll assembly. Take a look at the following
code:
Dim asm = Assembly.LoadFrom("People.dll")
Console.WriteLine("Enumerating types:")
For Each t In asm.GetTypes
Console.WriteLine("Type name: {0}", t.ToString)
Console.WriteLine(" Constructors:")
For Each constructor In t.GetConstructors
Console.WriteLine(" " + constructor.ToString)
Next
Console.WriteLine(" Methods:")
For Each method In t.GetMethods
Console.WriteLine(" " + method.ToString)
Next
Console.WriteLine(" Properties:")
For Each [property] In t.GetProperties
Console.WriteLine(" " + [property].ToString)
Next
Console.WriteLine(" Fields:")
For Each field In t.GetFields
Console.WriteLine(" " + field.ToString)
Next
Console.WriteLine(" Events:")
For Each [event] In t.GetEvents
Console.WriteLine(" " + [event].ToString)
Next
Next
You still get the instance of the desired assembly; then you can iterate types (or modules if preferred). The Assembly.GetTypes method returns an array of System.Type objects defined in the assembly that you can iterate for detailed information. The System.Type class exposes several GetX methods, in which X can stand for Constructors, Properties, Methods, Fields, and Events. Each of these methods returns a XInfo class instance, such as MethodInfo, PropertyInfo, FieldInfo, and so on. Each class exposes interesting properties about the inspected member for further information such as IsPrivate, IsPublic, or IsStatic.
Each XInfo class also exposes a Name property that returns the name of the member. In this case ToString was used instead of the name to return the full member signature.
|
Also, the System.Type class offers some useful properties enabling you to understand what kind of type you are inspecting such as IsClass, IsInterface, or IsEnum. The Namespace
property enables instead getting the namespace exposing the inspected
type. Notice that the preceding code inspects all types defined in the
specified assembly, including the ones that are usually part of My
Project. Also notice that Reflection considers properties’ getters and
setters such as methods that thus will be listed within this category.
For a better understanding, the following is an excerpt of the output
produced by the previously illustrated code:
Enumerating types:
Type name: People.My.MyApplication
Constructors:
Void .ctor()
Methods:
System.String GetEnvironmentVariable(System.String)
Microsoft.VisualBasic.Logging.Log get_Log()
Microsoft.VisualBasic.ApplicationServices.AssemblyInfo get_Info()
System.Globalization.CultureInfo get_Culture()
System.Globalization.CultureInfo get_UICulture()
Void ChangeCulture(System.String)
Void ChangeUICulture(System.String)
System.String ToString()
Boolean Equals(System.Object)
Int32 GetHashCode()
System.Type GetType()
Properties:
Microsoft.VisualBasic.Logging.Log Log
Microsoft.VisualBasic.ApplicationServices.AssemblyInfo Info
System.Globalization.CultureInfo Culture
System.Globalization.CultureInfo UICulture
Fields:
Events:
Type name: People.My.MyComputer
Constructors:
Void .ctor()
Methods:
Microsoft.VisualBasic.Devices.Audio get_Audio()
Microsoft.VisualBasic.MyServices.ClipboardProxy get_Clipboard()
Microsoft.VisualBasic.Devices.Ports get_Ports()
Microsoft.VisualBasic.Devices.Mouse get_Mouse()
Microsoft.VisualBasic.Devices.Keyboard get_Keyboard()
System.Windows.Forms.Screen get_Screen()
Microsoft.VisualBasic.Devices.Clock get_Clock()
Microsoft.VisualBasic.MyServices.FileSystemProxy get_FileSystem()
Microsoft.VisualBasic.Devices.ComputerInfo get_Info()
Microsoft.VisualBasic.Devices.Network get_Network()
System.String get_Name()
Microsoft.VisualBasic.MyServices.RegistryProxy get_Registry()
System.String ToString()
Boolean Equals(System.Object)
Int32 GetHashCode()
System.Type GetType()
Properties:
Microsoft.VisualBasic.Devices.Audio Audio
Microsoft.VisualBasic.MyServices.ClipboardProxy Clipboard
Microsoft.VisualBasic.Devices.Ports Ports
Microsoft.VisualBasic.Devices.Mouse Mouse
Microsoft.VisualBasic.Devices.Keyboard Keyboard
System.Windows.Forms.Screen Screen
Microsoft.VisualBasic.Devices.Clock Clock
Microsoft.VisualBasic.MyServices.FileSystemProxy FileSystem
Microsoft.VisualBasic.Devices.ComputerInfo Info
Microsoft.VisualBasic.Devices.Network Network
System.String Name
Microsoft.VisualBasic.MyServices.RegistryProxy Registry
Fields:
Events:
Type name: People.My.MyProject
Constructors:
Methods:
System.String ToString()
Boolean Equals(System.Object)
Int32 GetHashCode()
System.Type GetType()
Properties:
Fields:
Events:
Type name: People.My.MyProject+MyWebServices
Constructors:
Void .ctor()
Methods:
Boolean Equals(System.Object)
Int32 GetHashCode()
System.String ToString()
System.Type GetType()
Properties:
Fields:
Events:
Type name: People.Genders
Constructors:
Methods:
Boolean Equals(System.Object)
Int32 GetHashCode()
System.String ToString()
System.String ToString(System.String, System.IFormatProvider)
Int32 CompareTo(System.Object)
System.String ToString(System.String)
System.String ToString(System.IFormatProvider)
Boolean HasFlag(System.Enum)
System.TypeCode GetTypeCode()
System.Type GetType()
Properties:
Fields:
Int32 value__
People.Genders Male
People.Genders Female
Events:
Type name: People.IPerson
Constructors:
Methods:
System.String get_FirstName()
Void set_FirstName(System.String)
System.String get_LastName()
Void set_LastName(System.String)
Int32 get_Age()
Void set_Age(Int32)
People.Genders get_Gender()
Void set_Gender(People.Genders)
System.String BuildFullName()
Void add_InstanceCreated(InstanceCreatedEventHandler)
Void remove_InstanceCreated(InstanceCreatedEventHandler)
Properties:
System.String FirstName
System.String LastName
Int32 Age
People.Genders Gender
Fields:
Events:
InstanceCreatedEventHandler InstanceCreated
Type name: People.Person
Constructors:
Void .ctor()
Methods:
System.String get_FirstName()
Void set_FirstName(System.String)
People.Genders get_Gender()
Void set_Gender(People.Genders)
System.String get_LastName()
Void set_LastName(System.String)
Int32 get_Age()
Void set_Age(Int32)
Void add_InstanceCreated(InstanceCreatedEventHandler)
Void remove_InstanceCreated(InstanceCreatedEventHandler)
System.String BuildFullName()
System.String ToString()
Boolean Equals(System.Object)
Int32 GetHashCode()
System.Type GetType()
Properties:
System.String FirstName
People.Genders Gender
System.String LastName
Int32 Age
Fields:
Events:
InstanceCreatedEventHandler InstanceCreated
Type name: People.IPerson+InstanceCreatedEventHandler
Constructors:
Void .ctor(System.Object, IntPtr)
Methods:
System.IAsyncResult BeginInvoke(System.AsyncCallback, System.Object)
Void EndInvoke(System.IAsyncResult)
Void Invoke()
Void GetObjectData(System.Runtime.Serialization.SerializationInfo,
System.Runtime.Serialization.StreamingContext)
Boolean Equals(System.Object)
System.Delegate[] GetInvocationList()
Int32 GetHashCode()
System.Object DynamicInvoke(System.Object[])
System.Reflection.MethodInfo get_Method()
System.Object get_Target()
System.Object Clone()
System.String ToString()
System.Type GetType()
Properties:
System.Reflection.MethodInfo Method
System.Object Target
Fields:
Events:
Notice how also EventHandler
types, generated behind the scenes when you implement a simple event,
are inspected and illustrated. Also notice how the members’ signature
recalls the Intermediate Language syntax.
Reflecting a Single Type
Reflecting all types within
an assembly can be useful, but probably in most cases you will be
interested in reflecting a single type. To accomplish this you need the
instance of a System.Type; then invoke members described in the previous section. For example, imagine you want to inspect members from the Person class. You first get the type instance, and then you can perform reflection as demonstrated by the following code:
Dim myType As Type = (New People.Person).GetType
Console.WriteLine(" Methods:")
For Each method In myType.GetMethods
Console.WriteLine(" " + method.ToString)
Next
Console.WriteLine(" Properties:")
For Each [property] In myType.GetProperties
Console.WriteLine(" " + [property].ToString)
Next
Console.WriteLine(" Fields:")
For Each field In myType.GetFields
Console.WriteLine(" " + field.ToString)
Next
Console.WriteLine(" Events:")
For Each [event] In myType.GetEvents
Console.WriteLine(" " + [event].ToString)
Next
The preceding code produces the following result:
Methods:
System.String get_FirstName()
Void set_FirstName(System.String)
People.Genders get_Gender()
Void set_Gender(People.Genders)
System.String get_LastName()
Void set_LastName(System.String)
Int32 get_Age()
Void set_Age(Int32)
Void add_InstanceCreated(InstanceCreatedEventHandler)
Void remove_InstanceCreated(InstanceCreatedEventHandler)
System.String BuildFullName()
System.String ToString()
Boolean Equals(System.Object)
Int32 GetHashCode()
System.Type GetType()
Properties:
System.String FirstName
People.Genders Gender
System.String LastName
Int32 Age
Fields:
Events:
InstanceCreatedEventHandler InstanceCreated
For more details, the MSDN documentation on the System.Reflection namespace and the System.Type class are a good source of information on available members.
Reflection
is both a key topic and a powerful tool in the .NET developer toolbox.
By the way, you had the opportunity to understand how fragile your code
is in security terms because with a few lines of code anyone can see
types and members exposed by the assembly. Because preventing Reflection
is not possible, if you want to protect your code, you need to use an
obfuscation tool such as Dotfuscator (shipped with Visual Studio 2010)
that can add a more effective protection.