我正在尝试使用 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/