简体   繁体   English

在 Unity 中使用 Reflection.Emit 设置静态字段的值失败

[英]Setting the value of a static field using Reflection.Emit fails in Unity

I'm trying to set the value of a static field using Reflection.Emit (I don't have access to .NET 4's Expression.Assign because I'm stuck with Unity's .NET 3.5).我正在尝试使用Reflection.Emit设置静态字段的值(我无法访问 .NET 4 的Expression.Assign因为我坚持使用 Unity 的 .NET 3.5)。

My current code is as follows:我目前的代码如下:

public Action<TTarget, TField> GetSetter<TTarget, TField>(FieldInfo fieldInfo)
{
    DynamicMethod setterMethod = new DynamicMethod
    (
        "setter",
        typeof(void),
        new Type[] { typeof(TTarget), typeof(TField) },
        typeof(TTarget)
    );

    var setterIL = setterMethod.GetILGenerator();

    if (fieldInfo.IsStatic)
    {
        setterIL.Emit(OpCodes.Ldnull);
    }
    else
    {
        setterIL.Emit(OpCodes.Ldarg_0);
    }

    setterIL.Emit(OpCodes.Ldarg_1);
    setterIL.Emit(OpCodes.Stfld, fieldInfo);
    setterIL.Emit(OpCodes.Ret);

    return (Action<TTarget, TField>)setterMethod.CreateDelegate(typeof(Action<TTarget, TField>));
}

And then, I invoke the setter using:然后,我使用以下方法调用 setter:

public class Static
{
    public static int x;
}

var fieldInfo = typeof(Static).GetField("x");

var setter = GetSetter<Static, int>(fieldInfo);

setter.Invoke(null, 123);

I get this error message:我收到此错误消息:

NullReferenceException: Object reference not set to an instance of an object (wrapper dynamic-method) setter (...,int) NullReferenceException:未将对象引用设置为对象的实例(包装器动态方法)setter (...,int)

I thought loading null as the first argument ( Ldnull opcode) would fix it, but it doesn't seem to work.我认为将 null 作为第一个参数( Ldnull操作码)加载会解决它,但它似乎不起作用。 What am I doing wrong?我究竟做错了什么?

Update: it seems the exception is only triggered when the code runs from within Unity (latest, 5.5.0p4).更新:似乎只有在 Unity 中运行代码时才会触发异常(最新版本,5.5.0p4)。 In a .NET 3.5 console application created from Visual Studio, there is no issue.在从 Visual Studio 创建的 .NET 3.5 控制台应用程序中,没有问题。 Could there be an issue with Unity's Mono compiler? Unity 的 Mono 编译器会不会有问题?

Here is the full code to test from a Tools > Debug IL menu item in Unity.以下是从 Unity 中的Tools > Debug IL菜单项进行测试的完整代码。

using System;
using System.Reflection;
using System.Reflection.Emit;
using UnityEditor;

class Program
{
    public static Action<TTarget, TField> GetSetter<TTarget, TField>(FieldInfo fieldInfo)
    {
        DynamicMethod setterMethod = new DynamicMethod
        (
            "setter",
            typeof(void),
            new Type[] { typeof(TTarget), typeof(TField) },
            typeof(TTarget)
        );

        var setterIL = setterMethod.GetILGenerator();

        setterIL.Emit(OpCodes.Ldarg_0);
        setterIL.Emit(OpCodes.Ldarg_1);
        setterIL.Emit(OpCodes.Stfld, fieldInfo);
        setterIL.Emit(OpCodes.Ret);

        return (Action<TTarget, TField>)setterMethod.CreateDelegate(typeof(Action<TTarget, TField>));
    }

    public class Static
    {
        public static int x;
    }

    [MenuItem("Tools/Debug IL")]
    static void Debug()
    {
        var fieldInfo = typeof(Static).GetField("x");

        var setter = GetSetter<Static, int>(fieldInfo);

        setter.Invoke(null, 123);

        Debug.Log("Static field assignment succeeded.");
    }
}

Got it working with OpCodes.Stsfld (set static field) instead:让它与OpCodes.Stsfld (设置静态字段)一起使用:

if (fieldInfo.IsStatic)
{
    setterIL.Emit(OpCodes.Ldarg_0);
    setterIL.Emit(OpCodes.Stsfld, fieldInfo);
    setterIL.Emit(OpCodes.Ret);
}
else
{
    setterIL.Emit(OpCodes.Ldarg_0);
    setterIL.Emit(OpCodes.Ldarg_1);
    setterIL.Emit(OpCodes.Stfld, fieldInfo);
    setterIL.Emit(OpCodes.Ret);
}

The .NET runtime is probably more lenient than the Mono runtime behind the scenes (ie: it allows Stfld even for a static field, just ignoring the first parameter, whereas Mono doesn't), which would explain why the issue only occurred in Unity. .NET 运行时可能比幕后的 Mono 运行时更宽松(即:即使对于静态字段,它也允许Stfld ,只是忽略第一个参数,而 Mono 则不会),这将解释为什么该问题仅发生在 Unity 中.

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM