Sunday, October 07, 2007

Reflection

OK, for this example, I would need two projects, one is a c# console application and the other one is a c# DLL project.
The DLL will be called TestLateBinding and the Console App will be called Reflection.
Here are the classes that each one has:
DLL:
using System;
using System.Collections.Generic;
using System.Text;

namespace TestLateBinding
{
class TestClass
{
private void OnlyMethod()
{
Console.WriteLine("Printing from within OnlyMethod\n");
}
}
}

And the console application has Program.cs and TestDataType.

Here are the contents of TestDataType:

using System;
using System.Collections.Generic;
using System.Text;

namespace Reflection
{
public class TestDataType
{

public void print()
{
Console.WriteLine("Printing shit\n");
}

}

}

Here are the contents of Program.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace Reflection
{
class Program
{
static void Main(string[] args)
{
TestDataType testObject = new TestDataType();
Type objectType = testObject.GetType();

ConstructorInfo[] info = objectType.GetConstructors();
MethodInfo[] methods = objectType.GetMethods();

// get all the constructors
Console.WriteLine("Constructors:");
foreach (ConstructorInfo cf in info)
{
Console.WriteLine(cf);
}

Console.WriteLine();
// get all the methods
Console.WriteLine("Methods:");
foreach (MethodInfo mf in methods)
{
Console.WriteLine(mf);
}

MethodInfo info2 = objectType.GetMethod("print");
Console.WriteLine("Calling print:\n");
object[] parms = new object[1];
parms[0] = "d";
info2.Invoke(testObject, null);


Console.WriteLine("Testing Late Binding:\n");
Assembly a = Assembly.Load("TestLateBinding");
Console.WriteLine("Types in TestLateBinding assembly:\n");
foreach (Type t in a.GetTypes())
{
Console.WriteLine(t.Name);
if (t.Name.Equals("TestClass"))
{
MethodInfo info5 = t.GetMethod("OnlyMethod", BindingFlags.NonPublic | BindingFlags.Instance);

Object o = Activator.CreateInstance(t);
info5.Invoke(o, null);
}
}
Console.ReadLine();
}
}
}

Now in order for us to continue, you will have to build and then copy the TestLateBinding.dll from the TestLateBinding Debug Folder into the Debug folder of the console app, so we can access the members from the dll during runtime, without using compile time knowledge of the code in the library, in essence, without doing a reference to the library.

OK, so now to the code, in order for us to use reflection, we need the
using System.Reflection; reference.
So in these two lines:
TestDataType testObject = new TestDataType();
Type objectType = testObject.GetType();
I declare a new object of type TestDataType and then I use the objects, GetType method to assign it to the object objectType of type Type.
I know this is a silly example, but you can see how it would be helpful, if you were able to access methods, constructors and other information at runtime, so this is what I am trying to show here.
The next two lines get all constructors and all methods from the object using reflection:

ConstructorInfo[] info = objectType.GetConstructors();
MethodInfo[] methods = objectType.GetMethods();

I then print them out to the console. And I am also getting the method information for a particular method, in this case, print:

MethodInfo info2 = objectType.GetMethod("print");

In this case, the method does not accept any parameters, so I am just calling it:

info2.Invoke(testObject, null); However, if it required parameters, I could have done it so:
object[] parms = new object[1];
parms[0] = "d";
info2.Invoke(testObject, parms);
Note the above is actually quite silly, but it addresses the point that I am trying to make.

Next we can call an external assembly by using the Load method from the Assembly class:

Assembly a = Assembly.Load("TestLateBinding");

where "TestLateBinding" is the name of the assembly(our dll)

Then I am getting all types in the assembly and calling a private method from within the class:

foreach (Type t in a.GetTypes())
{
Console.WriteLine(t.Name);
if (t.Name.Equals("TestClass"))
{
MethodInfo info5 = t.GetMethod("OnlyMethod", BindingFlags.NonPublic | BindingFlags.Instance);

Object o = Activator.CreateInstance(t);
info5.Invoke(o, null);
}
}
Note the use of BindingFlags.NonPublic | BindingFlags.Instance, without those flags, the private method will not be reflected, I don;t know why, don't ask me.
If the method is public, you will not need to pass any flags.

I hope this helped

No comments: