Reflection
is a feature of .Net framework which allows navigating and interrogating
the type information of an assembly at runtime. This helps in building
dynamic systems with plug-in architecture with least effort. With this
feature, existing or newly generated code can be loaded and executed on
the fly which can help the developer to alter the code based on the state
of the assembly during runtime.
To understand
the concept of Reflection, it is essential to know about an Assembly file
which represents the unit of execution in .Net. An assembly file includes
all the following data that the runtime needs to execute:
Assembly
metadata (or manifest): includes name, version, strong name and culture
information of the assembly.
Type metadata: includes all namespaces and the types (classes) with their
individual properties, methods and constructors, if any.
Code: is the actual Intermediate Language (IL) code that gets converted
to machine code during execution.
Resources: includes images, strings, files, etc. used from the code.
Each assembly contains one or more modules that represent containers for
type information.
.Net classes for accessing Assembly information at runtime
The .Net framework provides rich set of classes for examining an assembly
and modules during runtime. The class, Assembly supports a number of static
methods for performing many operations related to assembly and also to
get assembly information during runtime. Some of the methods provided
include loading assembly into the AppDomain, fetch the name of calling
assembly, get its modules, types, etc.
In the context of Reflection, a module is a container within a single
assembly containing the types. The class, Module has methods to retrieve
and search for types based on certain criteria, get fields in the module,
etc.
Other assembly related information like copyright, company information,
etc. are usually stored as Attributes. To access these attributes, different
attribute classes (like AssemblyCopyRightAttribute, AssemblyCultureAttribute,
etc) have been designed to retrieve the information during runtime. To
get the individual attribute of an assembly, the method, GetCustomAttributes
method of Assembly class can be used.
To access the type information during runtime, the method, GetTypes method
of Assembly class can be used. Using the Type class, the different parts
of a Type like methods, properties, fields and events can be obtained.
For using all these classes, the namespace, System.Reflection have to
be included in the code.
Dynamic loading and execution of assembly
Dynamic code implies the code that does not have early access to it during
compilation and is dynamically loaded during runtime. The reflection system
allows creating objects dynamically from assemblies that have not been
referenced ahead of time. This is slightly risky and cumbersome than writing
type-safe compiler-checked code and is required sometimes.
Following are the steps for executing the code (invoking a member of an
object of a Type) dynamically:
Load the assembly (containing the given Type) by calling the Load
method of the object instance of Assembly class.
Create a new Type object of the required Type.
Create the ConstructorInfo object by calling the GetConstructor
method of the Type object with or without arguments.
By calling the Invoke method of the ConstructorInfo object, a new
object of the required type can be created.
Using a newly created MethodInfo object, the required method can
be called using the Invoke method by passing the method name and its argument
list.
Using a newly created PropertyInfo object and calling its GetValue
and SetValue methods, individual property of the object can be fetched
and set.
While invoking static methods, there is no need to dynamically create
an object. The other steps remain the same for invoking the method except
that NULL parameter needs to be passed (instead of the object in the non-static
case) while calling the Invoke method.
Dynamic code generation
Reflection helps to not only examine and load the assembly in runtime
but also define types and create new assemblies that can be for in-memory
consumption or serialized to disk for re-use. Many builder classes are
designed to build assemblies, modules and types at runtime which can even
allow persisting the assemblies to disk. Each of these builder classes
is derived from its info class counterpart.
These classes have been included in the namespace, System.Reflection.Emit.
Some of the builder classes used for dynamic code generation are AssemblyBuilder,
MethodBuilder, ModuleBuilder, ParameterBUilder, TypeBuilder and PropertyBuilder.
Following are the steps to be executed for creating a Type in a module
of a dynamically created assembly:
Create a dynamic assembly by calling the method, DefineDynamicAssembly
of the current AppDomain object instance. While doing so, options to whether
just run, save or run and save option for the assembly can be specified
to the call as per the requirement. This call returns an AssemblyBuilder
object.
Create a new ModuleBuilder object representing the module which
can hold the required Type by calling the method, DefineDynamicModule
of the AssemblyBuilder object along with the module name and assembly
file name.
Define a new TypeBuilder object by invoking the method, DefineType
of the ModuleBuilder object.
Create members for the newly created Type object using the definition
methods of the TypeBuilder object such as DefineConstructor, DefineMethod,
DefineProperty and DefineField.
To finally save the assembly, call the method, Save of the AssemblyBuilder
object.
Thus, Reflection is a powerful tool that can be utilized to a great extent
for providing software that can be controlled dynamically.