It's about two and a half years since I've started using .NET. Maybe I can say that I know it very well. I am now convinced that the only real advantage of .NET, if we compare it to Java, is in the existence of metadata, IL and Reflection.Emit, but very few programmers are willing or capable to go there. So let me present the example where we can use Reflection and Reflection.Emit to do something interesting.
Light non-trivial introductory example of reusability
There's a possibility that we need to add some functionality to the existing library but we are not in a position to change that library. One possible reason why we can't change that library could be that such a library is copyright protected. So we can't rely on our knowledge of IL and reengineer it. Luckily there is a way to achieve our goals and leave that library in its current shape.
Our example is an arithmetic tree. The purpose of arithmetic tree is to build OO representation of arithmetic expression. Its structure is a binary tree where leaf nodes represent numeric values and other nodes represent operators. We have two types of nodes - operators and numbers and they are instances of different classes. So we are dealing with a non-trivial example. Very nice example of arithmetic tree is presented in article Polymorphism in C++ by Bartosz Milewski, naturally it was written in C++ and we will have to translate it to C#.
public abstract class ArithmeticNode
{
public abstract double Calculate();
}
public class NumericNode:ArithmeticNode
{
double _num;
public NumericNode(double num){_num = num;}
public override double Calculate()
{
return _num;
}
}
public abstract class BinaryNode:ArithmeticNode
{
protected ArithmeticNode _Left;
protected ArithmeticNode _Right;
public BinaryNode(ArithmeticNode mLeft, ArithmeticNode mRight)
{
_Left = mLeft;
_Right = mRight;
}
}
public class AddNode:BinaryNode
{
public AddNode(ArithmeticNode mLeft, ArithmeticNode mRight):base(mLeft, mRight){}
public override double Calculate()
{
return _Left.Calculate() + _Right.Calculate();
}
}
public class MultiplyNode:BinaryNode
{
public MultiplyNode(ArithmeticNode mLeft, ArithmeticNode mRight):base(mLeft, mRight) {}
public override double Calculate()
{
return _Left.Calculate() * _Right.Calculate();
}
}
public class DeductNode:BinaryNode
{
public DeductNode(ArithmeticNode mLeft, ArithmeticNode mRight):base(mLeft, mRight){}
public override double Calculate()
{
return _Left.Calculate() - _Right.Calculate();
}
}
public class DivideNode:BinaryNode
{
public DivideNode(ArithmeticNode mLeft, ArithmeticNode mRight):base(mLeft, mRight){}
public override double Calculate()
{
return _Left.Calculate() / _Right.Calculate();
}
}
As we can see we have hierarchy of classes and one very elegant idea which is unfortunately not mine, I just translated it to C#. To build such a tree we usually produce some kind of parser, but to make things simple we will skip parser and build it manually, like this :
ArithmeticNode n1 =
new AddNode(new NumericNode(1.0), new NumericNode(2.0));
ArithmeticNode n2 = new MultiplyNode(n1, new NumericNode(3.0));
double x = n2.Calculate ();
That would be (1 + 2) * 3. With tree comes free recursion and to find out what is the result we just need to call Calculate method of the root node.
Now imagine that we are assigned to a project where we should make simple arithmetic calculator. So we need parser and GUI and we may use that library, lets imagine that library is free or that we will acquire license to use it. Unfortunately, specification says that arithmetic tree must print arithmetic expression. Since classes are not sealed we can make them visitable and use Visitor pattern. So we will inherit from each class and add accept method.
Just in time inheritance
Repetitive task of extending all classes and adding accept method is boring so we will automate it. Even better, we will use Reflection.Emit and extend dynamically only when and what is needed. To make invocation easier we will use interface. That was the project plan and now we can write the code. After few minutes the coding is done (OK I had something similar already written) and this is the code :
using
System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
public interface IVisitable
{
void Accept(IVisitor v);
}
public interface IVisitor
{
void Visit(IVisitable v);
}
public class VisitableWrapper
{
static System.Collections.Hashtable _assembliesHolder =
System.Collections.Hashtable.Synchronized(new System.Collections.Hashtable());
IVisitable _instance;
public object Current
{
get
{
return _instance;
}
set
{
_instance = build(value);
}
}
IVisitable build(object current)
{
Type restype;
IVisitable result;
Type currentType = current.GetType();
string theName = "Visitable" + currentType.Name;
if(!_assembliesHolder.ContainsKey(theName))
{
AssemblyName assemblyName = new AssemblyName();
assemblyName.Name = theName;
ILGenerator methodIL;
MethodBuilder mb;
AssemblyBuilder assembly = Thread.GetDomain().DefineDynamicAssembly
(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder module = assembly.DefineDynamicModule(theName+".dll");
TypeBuilder visitableClass = module.DefineType(theName, TypeAttributes.Public);
visitableClass.SetParent(currentType);
visitableClass.AddInterfaceImplementation(typeof(IVisitable));
Type[] arg = new Type[0];
ConstructorBuilder cb = visitableClass.DefineConstructor(MethodAttributes.Public,CallingConventions.Standard,null);
ILGenerator ctorIL = cb.GetILGenerator();
ConstructorInfo bc = currentType.GetConstructor(new Type[]{typeof(ArithmeticNode),typeof(ArithmeticNode)});
if(bc != null)
{
ctorIL.Emit(OpCodes.Ldarg_0);
ctorIL.Emit(OpCodes.Ldnull);
ctorIL.Emit(OpCodes.Ldnull);
ctorIL.Emit(OpCodes.Call, bc);
ctorIL.Emit(OpCodes.Ret);
}
else
{
bc = currentType.GetConstructor(new Type[]{typeof(double)});
ctorIL.Emit(OpCodes.Ldarg_0);
ctorIL.Emit(OpCodes.Ldc_R8,0.0);
ctorIL.Emit(OpCodes.Call, bc);
ctorIL.Emit(OpCodes.Ret);
arg = new Type[1]{typeof(IVisitor)};
}
arg = new Type[1]{typeof(IVisitor)};
mb = visitableClass.DefineMethod("Accept",MethodAttributes.Public|MethodAttributes.NewSlot|
MethodAttributes.HideBySig|MethodAttributes.Final|
MethodAttributes.Virtual,typeof(void),arg);
methodIL = mb.GetILGenerator();
ParameterBuilder paramBuilder = mb.DefineParameter(1,ParameterAttributes.None,"v");
methodIL.DeclareLocal(typeof(object[]));
methodIL.Emit(OpCodes.Ldarg_1);
methodIL.Emit(OpCodes.Callvirt,(new object().GetType()).GetMethod("GetType"));
methodIL.Emit(OpCodes.Ldstr,"Visit");
methodIL.Emit(OpCodes.Ldc_I4,0x100);
methodIL.Emit(OpCodes.Ldnull);
methodIL.Emit(OpCodes.Ldarg_1);
methodIL.Emit(OpCodes.Ldc_I4_1);
methodIL.Emit(OpCodes.Newarr,(new object()).GetType());
methodIL.Emit(OpCodes.Stloc_0);
methodIL.Emit(OpCodes.Ldloc_0);
methodIL.Emit(OpCodes.Ldc_I4_0);
methodIL.Emit(OpCodes.Ldarg_0);
methodIL.Emit(OpCodes.Stelem_Ref);
methodIL.Emit(OpCodes.Ldloc_0);
methodIL.Emit(OpCodes.Callvirt,typeof(System.Type).GetMethod("InvokeMember",
new Type[]{typeof(string),typeof(BindingFlags),typeof(Binder),typeof
(object),typeof(object[])}));
methodIL.Emit(OpCodes.Pop);
methodIL.Emit(OpCodes.Ret);
restype=visitableClass.CreateType();
result = (IVisitable)Activator.CreateInstance(restype);
_assembliesHolder.Add(theName,result);
}
else
{
result = (IVisitable)_assembliesHolder[theName];
restype = result.GetType();
}
FieldInfo[] fields = currentType.GetFields(BindingFlags.NonPublic |
BindingFlags.Instance | BindingFlags.Public | BindingFlags.Static );
for(int i = 0;i<fields.Length;i++)
{
fields[i].SetValue(result,fields[i].GetValue(current));
}
return result;
}
public void Accept(IVisitor v)
{
_instance.Accept(v);
}
}
As simple as that. Whatever other kind of node is added in the future to hierarchy, under the condition that it uses the prescribed constructor signature, it will work on it too. Interface is the same as I used in the article about Visitor on this site. If you're now wondering how I'm going to use Visitor on protected field, don't worry, Reflection will give us access to non-public fields. Here is the code for the Visitor and test :
using
System;
using System.Reflection;
class driver
{
static void Main ()
{
ArithmeticNode n1 = new DeductNode(new NumericNode(18.0), new NumericNode(11.0));
ArithmeticNode n2 = new MultiplyNode(n1, new NumericNode(2.0));
ArithmeticNode n3 = new DivideNode(new NumericNode(36.0),new NumericNode(6.0));
ArithmeticNode n4 = new AddNode(n2, n3);
double x = n4.Calculate ();
VisitableWrapper vis = new VisitableWrapper();
vis.Current = n4;
Visitor v =new Visitor();
vis.Accept(v);
System.Console.WriteLine(" = {0}",x);
}
}
class Visitor:IVisitor
{
public void Visit(IVisitable ve)
{
if(ve is BinaryNode)
{
System.Console.Write("(");
VisitableWrapper temp = new VisitableWrapper();
temp.Current = ve.GetType().GetField("_Left", BindingFlags.NonPublic
|BindingFlags.Instance).GetValue( ve );
temp.Accept(this);
switch(ve.GetType().ToString())
{
case "VisitableAddNode" : System.Console.Write(" + ");break;
case "VisitableMultiplyNode" : System.Console.Write(" * ");break;
case "VisitableDeductNode" : System.Console.Write(" - ");break;
case "VisitableDivideNode" : System.Console.Write(" / ");break;
}
temp.Current = ve.GetType().GetField("_Right", BindingFlags.NonPublic |BindingFlags.Instance).GetValue( ve );
temp.Accept(this);
System.Console.Write(")");
}
else
System.Console.Write(((NumericNode)ve).Calculate());
}
}
Maybe not very elegant, but it works and its very RAD. Yes, try this at home and all code in this article comes without any kind of warranty.
Now probably comes the question why we must use Visitor. For printing only we can leave it out, but tomorrow we may expect a request for XML serializable arithmetic tree or who knows what and then we will be happy to have Visitor. See it as quick-fix framework.
What if classes in hierarchy were sealed? Then we must do differently. The source of the problem is that we have no access to protected fields of BinaryNode. To gain access we may do something like this :
using
System;
using System.Reflection;
class GoodBinaryNode
{
BinaryNode _target;
Type targetType;
public GoodBinaryNode(BinaryNode target)
{
_target=target;
targetType = target.GetType();
}
public ArithmeticNode Left
{
get
{
FieldInfo _LeftInfo = targetType.GetField("_Left", BindingFlags.NonPublic
|BindingFlags.Instance);
return (ArithmeticNode)_LeftInfo.GetValue( _target );
}
}
public ArithmeticNode Right
{
get
{
FieldInfo _RightInfo = targetType.GetField("_Right", BindingFlags.NonPublic
|BindingFlags.Instance);
return (ArithmeticNode)_RightInfo.GetValue( _target );
}
}
public double Calculate()
{
return _target.Calculate();
}
public void Print()
{
System.Console.Write("(");
if(Left is BinaryNode)
{
GoodBinaryNode temp = new GoodBinaryNode((BinaryNode)Left);
temp.Print();
}
else
{
System.Console.Write(Left.Calculate());
}
switch(targetType.ToString())
{
case "AddNode" : System.Console.Write(" + ");break;
case "MultiplyNode" : System.Console.Write(" * ");break;
case "DeductNode" : System.Console.Write(" - ");break;
case "DivideNode" : System.Console.Write(" / ");break;
}
if(Right is BinaryNode)
{
GoodBinaryNode temp = new GoodBinaryNode((BinaryNode)Right);
temp.Print();
}
else
{
System.Console.Write(Right.Calculate());
}
System.Console.Write(")");
}
}
class driver
{
static void Main ()
{
ArithmeticNode n1 = new DeductNode(new NumericNode(18.0), new NumericNode(11.0));
ArithmeticNode n2 = new MultiplyNode(n1, new NumericNode(2.0));
ArithmeticNode n3 = new DivideNode(new NumericNode(36.0),new NumericNode(6.0));
ArithmeticNode n4 = new AddNode(n2, n3);
double x = n4.Calculate ();
GoodBinaryNode printRoot = new GoodBinaryNode((BinaryNode)n4);
printRoot.Print();
System.Console.WriteLine(" = {0}",x);
}
}
It resembles Decorator pattern a little and is much easier to code than that Visitor example, only if we have to pass instance of it to some method which expects one of original hierarchy types then we are in trouble. But then we are in trouble anyway. Naturally, we will skip Reflection.Emit exercise-we need to code only one class. It is much easier to write code in IL than to use Reflection.Emit.
Isn't that against OO principles to try to gain access to fields or methods which are not meant to be accessed from outside? Yes it is, if somebody makes fields inaccessible there could be reason for that, but if it helps me to write less code then I will defend myself with reusability argument. A bit of creative hacking (that kind of hacking which won't make public prosecutor interested in your activities) is always helpful.
Conclusion
As I demonstrated, Reflection and Reflection.Emit may be used to achieve interesting things. Naturally, we need to know IL to use Reflection.Emit. Possible areas where it could be used are AOP or Coordination Contracts. So highly configurable applications where about everything could be changed at run time are possible if appropriate OO patterns like Chain of Responsibility are used.
About two and a half years ago I went to Microsoft of South Africa (that is where I am staying, but I am not South African) to get my copy of .NET beta 1. They charged me about US$ 5 for three CDs. Today there is .NET 1.1, which I haven't got yet. Maybe I will go again to Microsoft of South Africa to get CDs, but this time I am not in a hurry. It looks to me that .NET started to lose its breath and advantages which it used to have over its competitors. For example, UDDI V2 API client side implementation for .NET is still in beta 1 stage! Lot of things have changed in the last two and a half years and in the world of programming changes are very fast.