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

标签 c# unity-game-engine reflection.emit

我正在尝试使用 Reflection.Emit 设置静态字段的值(我无权访问 .NET 4 的 Expression.Assign,因为我'我一直使用 Unity 的 .NET 3.5)。

我当前的代码如下:

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

然后,我使用以下方法调用 setter:

public class Static
{
    public static int x;
}

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

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

setter.Invoke(null, 123);

我收到此错误消息:

NullReferenceException: Object reference not set to an instance of an object (wrapper dynamic-method) setter (...,int)

我认为加载 null 作为第一个参数(Ldnull 操作码)可以修复它,但它似乎不起作用。我做错了什么?

更新:似乎只有当代码在 Unity 中运行时才会触发异常(最新,5.5.0p4)。在从 Visual Studio 创建的 .NET 3.5 控制台应用程序中,没有问题。 Unity 的 Mono 编译器有问题吗?

以下是通过 Unity 中的工具 > 调试 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.");
    }
}

最佳答案

可以使用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);
}

.NET 运行时可能比幕后的 Mono 运行时更宽松(即:即使对于静态字段,它也允许 Stfld,只是忽略第一个参数,而 Mono 则不允许),这可以解释为什么这个问题只发生在 Unity 中。

关于c# - 在 Unity 中使用 Reflection.Emit 设置静态字段的值失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41687763/

相关文章:

c# - 发出一个类覆盖两个具有相同名称的接口(interface)方法

visual-studio - 为什么当我创建新项目时,Unity Visual Studio 无法识别 "Using MlAgents",但在演示项目中可以识别它?

android - 如何防止在使用 Google Play 服务的多人在线 Android/iOS 游戏中作弊?

c# - 在 .Net 中使用 kernel32

javascript - 在包含 html 代码 jquery 的变量中更改 Id & Class

unity-game-engine - 如何以编程方式将图 block map 添加到网格

c# - 为什么 Calli 比委托(delegate)调用更快?

c# - 使用反射定义具有 typeof 嵌套类型的属性

c# - C# 中的 MySql 连接

c# - 以文化中立的方式将字符串拆分为单词