views:

64

answers:

3

I'm learning CIL by making my own functions at runtime with Reflection.Emit. I'm actually surprised how easy things have been up until now but I've hit something that I can't guess my way through and I can't find anything relative in the docs.

I'm trying to create a function that simply prints a very simple class I have defined. If I change my code to print strings, say, it works but it always fails to run when I pass an instance of my class A.

What's weird is I can comment out my function body and it still fails with a TargetInvocationException. It must be quite simple but I can't see what's up!

class A
{
    public override string  ToString()
    {
        return "AAA!";
    }
}

class Program
{
    static void Main(string[] args)
    {
        DynamicMethod func = new DynamicMethod("func", null, new Type[] { typeof(A) });

        ILGenerator il = func.GetILGenerator();

        //il.Emit(OpCodes.Ldarg_0);
        //il.Emit(OpCodes.Box, typeof(A));
        //il.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(A) }));

        il.Emit(OpCodes.Ret);

        func.Invoke(null, new object[] { new A() });

        Console.Read();
    }
}

What am I doing so wrong to make this raise an exception? Why does this only happen with my classes?

A: 

First - You shouldn't be emitting the Box opcode since A is a class and doesn't need to be boxed. Boxing is only for value types.

Second - The reason why it's failing is because the method doesn't have permission to access class A (it's not public). Either make A public OR You can instruct the JIT compiler to skip visibility checks by using this constructor and passing true to the skipVisibility parameter.

Brandon Cuff
But even with the lines commented an exception is raised.
I edited my response to include the real reason why this was failing.
Brandon Cuff
+1  A: 

It was quite silly actually. I spent several hours on this but when I followed Laurent Etiemble's advice and created a delegate the exception I got told me my class A wasn't public.

Solution: Add the public keyword before class A. Works perfectly. I knew it had to be something insanely simple.

A: 

The problem is simple but not-obvious. For a start don't box the argument as already pointed out. But the real problem is the A class is not public. The default binding for the Invoke function you are using (rather than the full one) is to only find publicly methods. Because A is a non-public class it means it fails to find your function (amazing I know) and fails.

tyranid
I realized almost as you posted. Hence my answer is along the same lines. This is the right answer!
Gotta love reflection sometimes, almost never gives you sensible answers, especially when you try object or string and it works just because they are public, grr :)
tyranid