我试图设置一个静态字段的值使用Reflection.Emit
(我没有访问.NET 4的Expression.Assign
,因为我坚持Unity的.NET 3.5)。使用Reflection.Emit设置静态字段的值在Unity中失败
我当前的代码如下:
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>));
}
然后,我调用使用二传手:
public class Static
{
public static int x;
}
var fieldInfo = typeof(Static).GetField("x");
var setter = GetSetter<Static, int>(fieldInfo);
setter.Invoke(null, 123);
我收到此错误信息:
的NullReferenceException:对象引用未设置为对象的实例 (包装器动态方法)setter(...,int)
我认为加载null作为第一个参数(Ldnull
操作码)会解决它,但它似乎并没有工作。我究竟做错了什么?
更新:它似乎只有在代码从Unity内部运行(最新,5.5.0p4)时才触发异常。在从Visual Studio创建的.NET 3.5控制台应用程序中,没有问题。 Unity的Mono编译器会有问题吗?
以下是在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.");
}
}
你确定这是确切的代码吗? 'GetSetter'不应该编译 - 你不能使用静态类型作为类型参数 –
Rob
另外,你不需要检查该字段是否是静态的。由于你的方法要求目标,(你提供它:'Invoke(null,123)') - 你应该总是发出'Ldarg_0'。这也会导致有人写'Invoke(notnullinstance,123)' - 哪个*应该抛出一个错误,但是会静静地将'123'设置为静态字段。 – Rob
你说得对,班上不是静态的,我匆匆做了一个简单的例子。根据[MSDN](https://msdn.microsoft.com/en-us/library/aya2tw8f(v = vs.100).aspx),你又是对的,'Ldarg_0'应该可以工作,但它不会'吨。 – Lazlo