Search code examples
c#dynamicreflectioninvokeemit

c#, reflection.emit trying hard


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: enter image description here

And the EditModule.dll: enter image description here

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?


Solution

  • 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);