سؤال

I've got the following class:

public class TestClass
{   
    public static readonly string HELLO = "Hello, ";

    public static string SayHello(string name)
    {
        return HELLO + name;
    } 
}

And I want to access the static field of HELLO via DynamicMethod. Standard reflection with GetValue works:

public static string GetViaInvoke()
    {
        Type tcType = typeof(TestClass);
        FieldInfo fi = tcType.GetField("HELLO");
        string result = fi.GetValue(null) as string;
        return result;
    }

But I need something similar to (OpCodes come from ILDasm of similar method):

public static string GetViaDynamicMethod()
    {
        Type tcType = typeof(TestClass);
        FieldInfo fi = tcType.GetField("HELLO");

        DynamicMethod dm = new DynamicMethod("getHello", typeof(string), Type.EmptyTypes);            
        ILGenerator iL = dm.GetILGenerator();

        iL.DeclareLocal(typeof(string));
        iL.Emit(OpCodes.Nop);
        iL.Emit(OpCodes.Ldsfld, fi);
        iL.Emit(OpCodes.Stloc_0);
        iL.Emit(OpCodes.Br_S, 0x09);
        iL.Emit(OpCodes.Ldloc_0);
        iL.Emit(OpCodes.Ret);

        Func<string> fun = dm.CreateDelegate(typeof(Func<string>)) as Func<string>;
        string result = fun();
        return result;
    }

The idea is preety simple, Dynamic methods work fine with the non-static fields (the ldfld opcode and this object), but when I try to acces the static field I receive exception:

System.InvalidProgramException was unhandled
  Message=InvalidProgramException
هل كانت مفيدة؟

المحلول

Basing IL code you write on decompiled code that does the same is a good idea, but you still need to understand what you're doing.

If you look at the documentation for Br_S, you'll see that you're supposed to use it with a Label, not int. I think the Br_S in your code branches off to an instruction at the byte offset 9, but I have no idea which instruction that is and you should never write code like this.

If you just want to load the value of the static field and return it, you don't need any local variables or branches. The following is enough:

iL.Emit(OpCodes.Ldsfld, fi);
iL.Emit(OpCodes.Ret);

What this does is that it loads the value on the evaluation stack and then immediately returns. It works, because when you return from a method that does return a value, the single value that's on on the evaluation stack is used as that return value.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top