我試圖設置一個靜態字段的值使用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