I wanted to try how the System.Reflection.Emit
works, but eventually I've stack with some problem. I wanted to create (using Emit
) some simple class, like this:
using System;
namespace emit
{
class Builder
{
private String name;
public Builder(String builderName)
{
name = builderName;
}
public override int GetHashCode()
{
return name.GetHashCode();
}
public override string ToString()
{
return this.name;
}
}
}
I've been through some tutorials and I've managed to create dynamic library. I've saved that library on my disk and then loaded it using reflection. I was able to filter it's types, constructors and methods. I also succesfully invoked the constructor passing desired string
. Yet, there's a problem when I'm trying to invoke any of methods- System.Reflection.TargetInvocationException
is thrown.
Here's how I implemented GetHashCode()
:
MethodBuilder mHashCode = tBuilder.DefineMethod("GetHashCode",
MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Virtual,
CallingConventions.HasThis, typeof(System.Int32), null);
mHashCode.SetImplementationFlags(MethodImplAttributes.Managed);
ILGenerator mHashGenerator = mHashCode.GetILGenerator();
MethodInfo defaultHashCode = typeof(object).GetMethod("GetHashCode");
mHashGenerator.Emit(OpCodes.Ldfld, simpleName);
mHashGenerator.Emit(OpCodes.Callvirt, defaultHashCode);
mHashGenerator.Emit(OpCodes.Ret);
tBuilder
is TypeBuilder
that I'm creating.
After saving the assembly and looking into with ILDSASM
it's like:
Unfortunately, this method won't invoke. Here's the code:
Type dType = asm.GetTypes()[0];
ConstructorInfo dConstructor = dType.GetConstructor(new Type[] { typeof(string) });
object dObject = dConstructor.Invoke(new object[] { "Pawel" });
MethodInfo[] dMethods = dType.GetMethods();
foreach(var mi in dMethods)
{
Console.WriteLine(mi.Name);
}
//method with `0` index is `GetHashCode()`
dMethods[0].Invoke(dObject, null);
Does anyone have an idea what I might be doing wrong?
This fragment
mHashGenerator.Emit(OpCodes.Ldfld, simpleName);
is trying to get the field value, but you forgot that in order to do that it needs an instance. Since the method you are building is an instance method, this is the hidden first argument of the method, so to get the value of this.name
you should use the following
mHashGenerator.Emit(OpCodes.Ldarg_0);
mHashGenerator.Emit(OpCodes.Ldfld, simpleName);