Reflection in .Net – Develop & Use Dynamically Pluggable Components (Part I)

One of the highlights of .Net and its Common Language Runtime (CLR) is its support for metadata. What does it actually mean is that with each assembly (.exe or .dll), .Net compilers store information about the types it contains. Using this metadata you can load an assembly dynamically (at runtime), get information about its containing types, instantiate these types and can even generate, compile and run some code at runtime!

This is an amazing power that .Net gives you through its reflection technology, which provides the ground to develop and use the pluggable components during the execution of application. Before we go on to further details, lets have some brief review of .Net assemblies, modules, types and how CLR manages them.

Brief overview of Assembly & Modules

An assembly is a physical collection of related modules and types used for deployment. There are two important points in this definition:

  1. While namespace is a logical collection of related types, an assembly is a physical collection of related types. Strictly speaking, an assembly exists in file system in the form of .exe or .dll file.
  2. An assembly is a deployment unit of your code. We deploy our application in the form of assemblies.
At this point, I think the concept of assembly is quite clear to you; but one thing that might be pondering in your mind is what actually the module mean then? In fact, an assembly may contain one or more modules. A module is again a physical collection of types.

You can compile some part of application or component in module (which gets stored in the file system as .netmodule file) and later combine all the related modules to make an assembly. So what exactly is the difference between a module and assembly? An assembly essentially contains something called ‘Manifest’ while a module does not. Finally, the manifest of an assembly contains the metadata with following information:

  • Versioning information of an assembly
  • An entry for each file that constitutes the assembly
  • An entry for each type in the assembly
  • An entry for each resources or other assemblies referenced (used) by this assembly
Most of the times, developers do not use multiple modules to pack their types but just a single module per assembly. Hence, an assembly usually contain types (classes, interfaces, structures, events) while types contain members (like variables, methods, properties).

Reflection

The dictionary meaning of the word ‘Reflection’ is ‘to look back to itself’. This is exactly what is meant by the term reflection in programming world. Reflection is an ability of application to look into the structure of itself. Using the reflection technology, we can retrieve the information about the contents of an assembly (constituting classes and interfaces).

Similarly we can drill down to get the members of these types like fields, methods, and properties. Not only this, but we can also instantiate these types and invoke the methods on these. We can even prepare, compile, and load assemblies at runtime!!!

===== Author’s Note: ===== The concept of reflection in .Net (C# & VB.Net) is similar to that in Java. The standard C++ does not support the reflection but there are a number of third party libraries available that make reflection possible in C++

Reflection in .Net

The System.Reflection is the root namespace that contains classes to implement the reflection. The Assembly class is the one which is used to represent the assembly in .Net environment. A simple program that loads the assembly and prints the names of the types that it contains is presented below:

using System;
using System.Reflection;
namespace ReflectionTest
{
	class Test
	{
		[STAThread]
		static void Main(string[] args)
		{
			// Load an assembly from file
			Assembly myAssembly = Assembly.LoadFrom("ReflectionTestAssembly.dll");
			// Get the types contained in the assembly and print their names
			Type []types = myAssembly.GetTypes();
			foreach(Type type in types)
			{
				Console.WriteLine(type.FullName);
			}
		}
	}
}


Here we have used the static method of the Assembly class ‘LoadFrom()’ which accepts the complete path and name of the assembly to load in string format. The method ‘LoadFrom()’ loads an assembly into CLR and returns an instance of type ‘Assembly’ which is an abstraction of the loaded assembly. Using this instance of type ‘Assembly’, we can do a lot of things.

In the above example, we have called the ‘GetTypes’ method of the ‘Assembly’ class which gives an array of all the types contained in an assembly. The types are represented by the ‘System.Type’ class. We then iterated the array and printed the names of all the types in the assembly on Console. When the above program is run, it produces the following output on console


ReflectionTestAssembly.MyInterface
ReflectionTestAssembly.MyClass
ReflectionTestAssembly.OurClass
Press any key to continue



Some important methods of the Assembly class are:

MethodDescription
CreateInstance(string)Creates an instance of the specified type in the assembly and returns its object
GetAssembly(Type) staticThis static method returns the assembly that contains the specified type.
GetCallingAssembly() staticThis static method returns the assembly of the method from which the method containing this code has been called.
GetExecuingAssembly() staticThis static method returns the assembly that contains this code.For example, if we call a method in assembly B from assembly A, and the method in assembly B contains both GetCallingAssembly() and GetExecutingAssembly() then, GetCallingAssembly() will return Assembly A while GetExecutingAssembly() will return Assembly B
GetModule(string)Returns the specified module that is part of this assembly
GetModules()Returns an array of all the modules of this assembly
GetReferencedAssemblies()Returns an array of all the assemblies referenced by this assembly. The type of array is AssemblyName
Load(AssemblyName) overloaded staticThis static method loads an assembly into CLR provided its AssemblyName
LoadFrom(string) staticThis static method load an assembly into CLR from the specified file


Similarly, some important properties and methods of the Type class are presented in the following table

PropertyDescription
AssemblyReturns the assembly of this type
NamespaceReturns the namespace of this type
AttriutesReturns the attributes associated with this type
BaseTypeReturns the direct base class of the type
FullNameReturns the fully qualified name of the type including namespace
IsClassReturns a Boolean value representing whether a type is a class or not
IsInterfaceReturns a Boolean value representing whether a type is an interface or not
IsEnumReturns a Boolean value representing whether a type is an enumeration or not
IsCOMObjectReturns a Boolean value representing whether a type is a COM object or not
IsAbstractReturns a Boolean value representing whether a type is an abstract class or not
IsByRefReturns a Boolean value representing whether a type is referenced type or not (i.e., passed by reference)
IsValueTypeReturns a Boolean value representing whether a type is value type or not (i.e., passed by value)
IsPublicReturns a Boolean value representing whether a type is public or not
IsSealedReturns a Boolean value representing whether a type is sealed (can not be inherited) or not
IsSerializableReturns a Boolean value representing whether a type is serializable (can be passed to a stream) or not


And then some of the important methods of Type class are:

MethodDescription
GetInterfaces()Returns all the interfaces implemented or inherited by the type
IsSubclassOf(Type)Returns Boolean representing whether this type is derived from the type supplied in the parameter
GetNestedTypes()Returns all the types nested in this type
GetConstructor(Type[])overloadedReturns an object of type ConstructorInfo representing the constructor of the type which takes the parameter types matching the supplied types
GetConstructors()Returns an array of ConstructorInfo object representing the constructors of the type.
GetMembers()Returns an array of MemberInfo object representing the members (fields, properties, methods) of this type
GetFields()Returns an array of FieldInfo object representing the fields of this type
GetProperties()Returns an array of PropertyInfo object representing the properties of this type
GetMethods()Returns an array of MethodInfo object representing the methods of this type
GetEvents()Returns an array of EventInfo object representing the events of this type
InvokeMember()Invoke a member (constructor, field, property and method) on the specified object type represented by this instance
IsInstanceOfType()Returns a boolean representing whether the current type is derived from the specified type or not


Now as we have seen the few key methods and properties of these classes; lets see some sample code to perform these tasks.

Exploring the hierarchy of types in an assembly

In the following sample code, we will demonstrate how we can explore the hierarchy of types in an assembly. We will get all the types contained in an assembly, list these types along with their parent class and implementing interfaces. The source code of the program looks like:

using System;
using System.Reflection;
namespace ReflectionTest
{
	class Test
	{
		[STAThread]
		static void Main(string[] args)
		{
			// Load an assembly from file
			Assembly myAssembly = Assembly.LoadFrom("ReflectionTestAssembly.dll");
			// Get the types contained in the assembly and print their names
			Type []types = myAssembly.GetTypes();
			foreach(Type type in types)
			{
				if(type.IsInterface)
				{
					Console.WriteLine("\r\n" + type.FullName + " is an interface");
				}
				else if(type.IsClass)
				{
					// Get and print the list of its parent class and interfaces
					Console.WriteLine("\r\n" + type.FullName + 
" is a class whose base class is " + 
type.BaseType.FullName);
					Console.WriteLine("\t It implements the following interfaces");
					foreach(Type interfaceType in type.GetInterfaces())
					{
						Console.WriteLine("\t\t {0}", interfaceType.FullName);
					}
				}
			}
			Console.ReadLine();
		}
	}
}
Here we got the list of types in the assembly just as we did in the previous example. We iterated through the types, checked whether it is an interface or a class using the IsInterface and IsClass properties of the Type class

	if(type.IsInterface)
	{
		...
	}
	else if(type.IsClass)
	{
		...
	}
If the type is a class, we retrieved the name of its immediate parent class. Since all classes in .Net are implicitly inherited from the Object class and no class can have more than one immediate parent class, hence we are sure that the property BaseType will return exactly one type instance.

Console.WriteLine("\r\n" + type.FullName + " is a class whose base class is " 
+ type.BaseType.FullName);


On the contrary, a class may or may not implement interfaces, so we iterated through the list of implemented interfaces (if any) using the GetInterfaces() method which returns an array of Type

foreach(Type interfaceType in type.GetInterfaces())
{
	...
}


The complete source code of the above example can be downloaded from here

Exploring the contents of types

Now when we have learnt how to explore the contents of assembly, lets learn how to get the contents of types. The following sample will get the list of members (fields, properties and methods) of a class ‘MyClass’ which is defined in the assembly ‘ReflectionTestAssembly’ as

using System;
using System.Reflection;
namespace ReflectionTestAssembly
{
	public interface MyInterface
	{
		void Fun(int iParam);
	}
	public class MyClass : MyInterface
	{
		private int number;
		public readonly double PI = 3.14;
		public MyClass()
		{
			Console.WriteLine("MyClass instantiated");
			number = 6;
		}
		public int Number
		{
			get { return number; }
			set { number = value; }
		}
		public int GenerateRandom()
		{
			Random r = new Random();
			return r.Next();
		}
		public void Fun(int iParam)
		{
			Console.WriteLine("The parameter supplied is fortunately {0}", iParam);
		}
		public void SayGreeting()
		{
			Console.WriteLine("Hello World!");
		}
	}
}


Next



 

Other Views

corner

Recent Jobs

Official Programmer's Heaven Blogs
Web Hosting | Browser and Social Games | Gadgets

Popular resources on Programmersheaven.com
Assembly | Basic | C | C# | C++ | Delphi | Flash | Java | JavaScript | Pascal | Perl | PHP | Python | Ruby | Visual Basic
© Copyright 2011 Programmersheaven.com - All rights reserved.
Reproduction in whole or in part, in any form or medium without express written permission is prohibited.
Violators of this policy may be subject to legal action. Please read our Terms Of Use and Privacy Statement for more information.
Operated by CommunityHeaven, a BootstrapLabs company.