MSIL - 2. Implementing Interfaces
Following on from my previous blog post I intend to show you how to take the basics that I presented and extend them to become much more useful. One of the problems with the previous example was that there was absolutely no way when writing the test code to use the object that had been dynamically created without using even more reflection. The answer to this is to start implementing interfaces so that the compiled code knows the interface for the dynamically generated methods.
Once again, all of the source code can be downloaded from the
here.
The interface
I am going to continue on with the Calculator theme:
public interface Calculator
{
int Add(int a, int b);
int Subtract(int a, int b);
int Multiply(int a, int b);
int Divide(int numerator, int denominator);
}
Implementing a method
In my last blog post I showed you how to create a method using
Reflection.Emit, so staying with the
Add() method I will show you how I would create the method based on the interface. The IL inside the method will not change so the primary changes will come from simply defining the method.
1. Mark the class as implementing the interface
var interfaceType = typeof(Calculator);
...
typeBuilder.AddInterfaceImplementation(interfaceType);
2. Define the method
The definition of the method is almost identical to the definition of the method shown in the previous post:
var interfaceAddMethod = interfaceType.GetMethod("Add");
var methodBuilder = typeBuilder.DefineMethod("Add", MethodAttributes.Public |
MethodAttributes.Virtual |
MethodAttributes.Final |
MethodAttributes.NewSlot |
MethodAttributes.HideBySig);
methodBuilder.SetReturnType(interfaceAddMethod.ReturnType);
var parameters = interfaceAddMethod
.GetParameters()
.Select(parameter => parameter.ParameterType)
.ToArray();
methodBuilder.SetParameters(parameters);
...
var il = methodBuilder.GetILGenerator();
...
typeBuilder.DefineMethodOverride(methodBuilder, interfaceAddMethod);
One of the most interesting things that have discovered on my journey through IL I realised that the implementing method is marked as
Virtual and
Final in order to implement the interface. The attribute
HideBySig indicates that this method hides all methods in base classes and interfaces with the same signature and the attribute
NewSlot tells the compiler to allocate a new spot in the V-Table.
A lot of work has already been done for us in defining the interface, the parameters and the return type are defined for us. After generating the IL for the method there is one final call to make
typeBuilder.DefineMethodOverride() passing in the generated method and the method from the interface that it is overriding.
3. Defining Multiply and Divide as Not Implemented
For now I do not need to implement these methods and I would like to throw a
NotImplementedException rather than worrying about writing the IL now. The method bodies become very simple:
var il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldstr, "The Multiply method has not been implemented yet");
il.Emit(OpCodes.Newobj, typeof(NotImplementedException).GetConstructor(new [] {typeof(string)}));
il.Emit(OpCodes.Throw);
It simply pushes the string onto the stack and uses it to create a new exception that is then immediately throws.
What is next?
I have shown you how to implement an interface and the basic mechanisms of creating a new object (in this case an exception). The next stage is to take the calculator implementation further using the
Divide method which will take us into writing an
if statement to detect if the denominator is 0.
Tags: