2017-01-17 11 views
2

Reflection.Emitを使用して静的フィールドの値を設定しようとしています(.NET 4のExpression.Assignにアクセスできない.NET 3.5)。次のようにUnityでReflect.Emitを使用して静的フィールドの値を設定すると、エラーが発生する

私の現在のコードは次のとおりです。

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)

最初の引数(Ldnullオペコード)がnullを読み込んでいると思っても問題は解決しませんが、動作していないようです。私は間違って何をしていますか?

更新:コードが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."); 
    } 
} 
+1

正確なコードは本当ですか? 'GetSetter 'はコンパイルすべきではありません。型引数として静的型を使うことはできません。 – Rob

+2

また、フィールドが静的であるかどうかを確認する必要もありません。あなたのメソッドはターゲットを尋ねるので( 'Invoke(null、123)')、 'Ldarg_0'を出すだけです。これはまた、誰かが 'Invoke(notnullinstance、123)'を書くときに問題を引き起こします。*これはエラーをスローするべきですが、静的フィールドに '123 'を静かに設定します。 – Rob

+0

あなたはそうです、クラスは静的ではありません。私は急いで簡単な例をまとめます。 [MSDN](https://msdn.microsoft.com/en-us/library/aya2tw8f(v = vs.100).aspx)によれば、あなたはもう一度、Ldarg_0が動作するはずですが、 t。 – Lazlo

答えて

2

ではなく(フィールドを設定)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で問題が発生した理由を説明します。

関連する問題