|
Every
managed application in .NET is deployed as an assembly and
every assembly defines certain types. These types are revealed
in the form of metadata. Metadata is the data written in binary
form in Portable Executable (PE) file. Metadata gives a detailed
description of types and external assemblies that are used
by the types. For example, metadata of a class would completely
define the class including its methods, parameters of methods,
calling convention, classs data members and visibility
of class members. We can read this information from metadata
using reflection. The .NET base class library has provided
namespaces and classes for this encapsulated in System.Reflection
namespace. Using reflection we can read types from assembly,
create instances of types dynamically, invoke methods, etc.
Before we discuss how to use reflection technology, we would
see how types are arranged in metadata and what purpose metadata
serves.
One of the biggest benefits of .NET is that code written for
it can be used by different languages. A function defined
in VC++ can be called from VB. But as both the languages use
different syntax, there must be some common format to enable
interoperability. This common format is metadata. Another
benefit of using metadata is that it eliminates the need of
specifying the import library in the linker options. This
is possible because metadata is written in the same EXE or
DLL file as the code itself. The information in metadata is
arranged in the hierarchy shown in Diagram 1.

Diagram
1
Reflection allows us to either read data from assembly or
write data to assemblies. The System.Reflection namespace
contains classes to read an assembly, whereas, System.Reflection.Emit
namespace contains classes to write to the assembly. In this
article, we would firstly create an application that lists
all the types of selected assembly. Then, on selecting a type,
its members get displayed.
Create a Windows Application and design a form as shown in
the following figure.

The variables and events of different controls in the form
are given in the following table.
Control Variable Event
TextBox aname
Load Button load Click
Types ListBox list SelectedValueChanged
Methods and listmethods
Constructors
ListBox
Properties listprop
ListBox
Events ListBox listeve
Also add the OpenFileDialog control to the form. On clicking
the Load button the standard Open
dialog box gets displayed. If the user selects an assembly
and clicks the OK button, its path gets displayed in the text
box, the assembly gets loaded in memory and its types get
displayed in the list box. To do this, we have added the Click
event handler as shown below.
private void load_Click ( object sender, System.EventArgs e )
{
openFileDialog1.InitialDirectory =
@C:\WINDOWS\Microsoft.NET\ Framework\v1.0.3705 ;
openFileDialog1.Filter = Assemblies (*.dll)|*.dll ;
if ( openFileDialog1.ShowDialog( ) == DialogResult.OK )
{
list.Items.Clear( ) ;
aname.Text = openFileDialog1.FileName ;
Assembly a ;
try
{
a = Assembly.LoadFrom
(aname.Text);
tarray = a.GetExportedTypes( ) ;
}
catch ( Exception ex )
{
MessageBox.Show ( Error in loading assembly ) ;
return;
}
foreach ( Type type in tarray )
list.Items.Add ( type.FullName ) ;
list.SetSelected ( 0, true ) ;
}
}
Here,
firstly we have set the default folder where all .NET framework
assemblies are stored. We have also set the Filter property
to .dll so that only assemblies would be listed
in the dialog. To load the selected assembly, we have read
the assembly path from the text box and called the LoadFrom(
) static method of the Assembly class. This method returns
a reference to the Assembly object. Using this reference,
we have called the GetExportedTypes( ) method that returns
an array of types available in the assembly. tarray is an
array of Type objects. Add this array as a private data member.
The types returned by the GetExportedTypes( ) method are added
to the list box in the foreach loop using the Add( ) method.
Now let us write the code to display type members in respective
list boxes, if any type is selected. The SelectedValueChanged
event handler is given below.
private void selchange (object sender,System.EventArgs e)
{
int index = list.SelectedIndex;
Type t = tarray[index];
listmethods.Items.Clear();
listprop.Items.Clear();
listeve.Items.Clear();
ConstructorInfo [] ci = t.GetConstructors();
foreach (ConstructorInfo c in ci)
listmethods.Items.Add (c);
PropertyInfo [] pi = t.GetProperties();
foreach (PropertyInfo p in pi)
listprop.Items.Add(p);
EventInfo [] ei = t.GetEvents();
foreach (EventInfo ev in ei)
listeve.Items.Add(ev);
MethodInfo [] mi = t.GetMethods(BindingFlags.Instance |
BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic);
String str = ;
foreach (MethodInfo m in mi)
{
if (t == m.DeclaringType)
{
str = m.Name;
str += ( ;
ParameterInfo [] pif = m.GetParameters() ;
foreach (ParameterInfo p in pif)
{
str += p.ParameterType;
str += ;
str += p.Name;
str += , ;
}
int c = str.LastIndexOf (,);
if (c != -1)
str = str.Remove (c, 1);
str += );
listmethods.Items.Add (str);
}
}
}
To display members we must first obtain the item selected
from the list box. We have done so by using the SelectedIndex
property of the ListBox class. We have retrieved the selected
type from the tarray array.
The Type class contains various methods to access members
of a type. We have used the same to read constructors, methods,
properties and events of the selected type. These methods
return a reference to an array containing respective members.
We have listed the members in the respective list boxes. To
the GetMethods( ) method we have passed certain flags. These
flags indicate that we intend to read the instance methods,
methods that are not inherited from base type and methods
that are public as well as non-public. We can mention this
flag in all the other methods as well. Since method name and
its parameters need to be read separately, we have used string
concatenation to form a string that looks like a prototype
declaration of method. Again, we have listed only those methods
that directly belong to this type. We have used the DeclaringType
property of the MethodInfo class.
We would now see a small example that loads a user-created
assembly, obtains a class type, creates its instance and invokes
a method defined in the assembly. Here is the code...
Assembly a = Assembly.LoadFrom(c:\\mymath.dll);
Type t = a.GetType (mymath);
MethodInfo m = t.GetMethod(add);
Object obj = Activator.CreateInstance (t);
Object[ ] arg = new Object [2];
arg[0] = 10;
arg[1] = 20;
m.Invoke(obj, arg);
mymath is the name of class written in mymath.dll.
To the GetMethod( ) method we have passed the name of the
method we want to invoke. Next, by calling the CreateInstance(
) method we have created an object of type stored in t and
collected its reference in obj. The add( ) method takes two
integers. So, we have created an array of objects arg. Lastly,
we have called the add( ) method by using the Invoke( ) method.
The add( ) method adds the two integers passed to it and displays
the result.
 |
Yashavant
Kanetkar, one of the first Express Computer columnists,
is an established software expert, speaker and author
with several best-sellers to his credit, including titles
like “Let Us C” and the “Fundas” series. Contact him at
kanet@nagpur.dot.net.in |
|