30

プロパティについてはGetGetMethod、次のGetSetMethodことができます。

Getter = (Func<S, T>)Delegate.CreateDelegate(typeof(Func<S, T>), 
                                             propertyInfo.GetGetMethod());

Setter = (Action<S, T>)Delegate.CreateDelegate(typeof(Action<S, T>), 
                                               propertyInfo.GetSetMethod());

しかし、どうすればFieldInfosについて行くことができますか?

GetValueandへのデリゲートを探しているわけではありませんSetValue(つまり、毎回リフレクションを呼び出すことになります)。

Getter = s => (T)fieldInfo.GetValue(s);
Setter = (s, t) => (T)fieldInfo.SetValue(s, t);

しかし、CreateDelegateここにアプローチがあるとしたら?つまり、割り当ては値を返すので、割り当てをメソッドのように扱うことはできますか? もしそうなら、それのためのMethodInfoハンドルはありますか?つまり、メンバー フィールドから値を設定および取得する権利をメソッドに渡して、フィールドを直接読み書きできるデリゲートを取得するにはどうすればよいでしょうか。MethodInfoCreateDelegate

Getter = (Func<S, T>)Delegate.CreateDelegate(typeof(Func<S, T>), fieldInfo.??);
Setter = (Action<S, T>)Delegate.CreateDelegate(typeof(Action<S, T>), fieldInfo.??);

式を作成してコンパイルすることはできますが、もっと単純なものを探しています。最後に、以下に示すように、尋ねられた質問に対する回答がない場合、式ルートに進んでもかまいません。

var instExp = Expression.Parameter(typeof(S));
var fieldExp = Expression.Field(instExp, fieldInfo);
Getter = Expression.Lambda<Func<S, T>>(fieldExp, instExp).Compile();
if (!fieldInfo.IsInitOnly)
{
    var valueExp = Expression.Parameter(typeof(T));
    Setter = Expression.Lambda<Action<S, T>>(Expression.Assign(fieldExp, valueExp), instExp, valueExp).Compile();
}

それとも存在しないものを求めているのでしょうか (そのようなものはまだどこにも見たことがありません) ?

4

8 に答える 8

30

Peter Ritchie が提案したように、実行時に独自のコードをコンパイルできます。メソッドは、デリゲートを初めて呼び出すとすぐにコンパイルされます。したがって、最初の呼び出しは遅くなりますが、その後の呼び出しは、アンマネージ ポインター/ユニオンなしで .NET で得られる速度と同じくらい速くなります。最初の呼び出しを除いて、デリゲートは直接 FieldInfo よりも約 500 倍高速です。

class DemoProgram
{
    class Target
    {
        private int value;
    }

    static void Main(string[] args)
    {
        FieldInfo valueField = typeof(Target).GetFields(BindingFlags.NonPublic| BindingFlags.Instance).First();
        var getValue = CreateGetter<Target, int>(valueField);
        var setValue = CreateSetter<Target, int>(valueField);

        Target target = new Target();

        setValue(target, 42);
        Console.WriteLine(getValue(target));
    }

    static Func<S, T> CreateGetter<S, T>(FieldInfo field)
    {
        string methodName = field.ReflectedType.FullName + ".get_" + field.Name;
        DynamicMethod setterMethod = new DynamicMethod(methodName, typeof(T), new Type[1] { typeof(S) }, true);
        ILGenerator gen = setterMethod.GetILGenerator();
        if (field.IsStatic)
        {
            gen.Emit(OpCodes.Ldsfld, field);
        }
        else
        {
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldfld, field);
        }
        gen.Emit(OpCodes.Ret);
        return (Func<S, T>)setterMethod.CreateDelegate(typeof(Func<S, T>));
    }

    static Action<S, T> CreateSetter<S,T>(FieldInfo field)
    {
        string methodName = field.ReflectedType.FullName+".set_"+field.Name;
        DynamicMethod setterMethod = new DynamicMethod(methodName, null, new Type[2]{typeof(S),typeof(T)},true);
        ILGenerator gen = setterMethod.GetILGenerator();
        if (field.IsStatic)
        {
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(OpCodes.Stsfld, field);
        }
        else
        {
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(OpCodes.Stfld, field);
        }
        gen.Emit(OpCodes.Ret);
        return (Action<S, T>)setterMethod.CreateDelegate(typeof(Action<S, T>));
    }
}

構造体は値渡しであることに注意してください。つまりAction<S, T>、構造体が最初の引数として値渡しされている場合、構造体のメンバーを変更するために使用することはできません。

于 2013-04-25T19:12:44.293 に答える
2

を使用するかどうかはわかりませんがExpression、なぜ反射を避けるのですか? のほとんどの操作はExpressionリフレクションに依存しています。

GetValueSetValue自体はフィールドのget methodandset methodですが、特定のフィールド用ではありません。

フィールドはプロパティとは異なり、フィールドであり、それぞれに対して get/set メソッドを生成する理由はありません。ただし、タイプはフィールドによって異なる場合があるため、GetValueとは差異としてSetValue定義されます。つまり、それをオーバーライドするすべてのクラス (まだリフレクション) に対して、同一のシグネチャ内にある必要があります。parameter/return valueobjectGetValue

それらを入力しない場合は、次のコードで行う必要があります。

public static void SomeMethod(FieldInfo fieldInfo) {
    var Getter=(Func<object, object>)fieldInfo.GetValue;
    var Setter=(Action<object, object>)fieldInfo.SetValue;
}

ただし、必要に応じて、制約付きの方法があります。

public static void SomeMethod<S, T>(FieldInfo fieldInfo)
    where S: class
    where T: class {
    var Getter=(Func<S, object>)fieldInfo.GetValue;
    var Setter=(Action<S, T>)fieldInfo.SetValue;
}

Getterまだ であるという理由から、以下Func<S, object>を確認することをお勧めします。

C# における共分散と反分散、第 3 部: Lippert 氏のブログのメソッド グループ変換分散。

于 2013-04-24T12:26:04.673 に答える
1

オブジェクトを操作しているときにデリゲートを作成する別のオプションを次に示します (フィールドの特定のタイプがわからない場合)。フィールドが構造体の場合は遅くなりますが(ボクシングのため)。

public static class ReflectionUtility
{
    public static Func<object, object> CompileGetter(this FieldInfo field)
    {
        string methodName = field.ReflectedType.FullName + ".get_" + field.Name;
        DynamicMethod setterMethod = new DynamicMethod(methodName, typeof(object), new[] { typeof(object) }, true);
        ILGenerator gen = setterMethod.GetILGenerator();
        if (field.IsStatic)
        {
            gen.Emit(OpCodes.Ldsfld, field);
            gen.Emit(field.FieldType.IsClass ? OpCodes.Castclass : OpCodes.Box, field.FieldType);
        }
        else
        {
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Castclass, field.DeclaringType);
            gen.Emit(OpCodes.Ldfld, field);
            gen.Emit(field.FieldType.IsClass ? OpCodes.Castclass : OpCodes.Box, field.FieldType);
        }
        gen.Emit(OpCodes.Ret);
        return (Func<object, object>)setterMethod.CreateDelegate(typeof(Func<object, object>));
    }

    public static Action<object, object> CompileSetter(this FieldInfo field)
    {
        string methodName = field.ReflectedType.FullName + ".set_" + field.Name;
        DynamicMethod setterMethod = new DynamicMethod(methodName, null, new[] { typeof(object), typeof(object) }, true);
        ILGenerator gen = setterMethod.GetILGenerator();
        if (field.IsStatic)
        {
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(field.FieldType.IsClass ? OpCodes.Castclass : OpCodes.Unbox_Any, field.FieldType);
            gen.Emit(OpCodes.Stsfld, field);
        }
        else
        {
            gen.Emit(OpCodes.Ldarg_0);
            gen.Emit(OpCodes.Castclass, field.DeclaringType);
            gen.Emit(OpCodes.Ldarg_1);
            gen.Emit(field.FieldType.IsClass ? OpCodes.Castclass : OpCodes.Unbox_Any, field.FieldType);
            gen.Emit(OpCodes.Stfld, field);
        }
        gen.Emit(OpCodes.Ret);
        return (Action<object, object>)setterMethod.CreateDelegate(typeof(Action<object, object>));
    }
}
于 2016-05-16T18:18:38.307 に答える