Gibt es eine Möglichkeit, einen Delegaten zu erstellen, um Werte für eine FieldInfo abzurufen und festzulegen?

c# delegates expression-trees fieldinfo setvalue

Frage

Für Eigenschaften gibt es GetGetMethod und GetSetMethod so dass ich GetSetMethod tun kann:

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

und

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

Aber wie gehe ich über FieldInfo s vor?

Ich suche keine Delegierten für GetValue und SetValue (was bedeutet, dass ich jedes Mal die Reflektion aufrufen werde)

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

aber wenn es einen CreateDelegate Ansatz hier gibt? Ich meine, da Zuweisungen einen Wert zurückgeben , kann ich Zuordnungen wie eine Methode behandeln? Wenn ja, gibt es ein MethodInfo Handle dafür? Mit anderen Worten, wie übergebe ich die richtige MethodInfo des Festlegens und CreateDelegate eines Werts aus einem Mitgliedsfeld an die CreateDelegate Methode, sodass ich einen Delegaten zurückbekomme, mit dem ich Felder direkt lesen und schreiben kann?

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

Ich kann Ausdruck erstellen und kompilieren, aber ich suche nach etwas einfacherem. Am Ende macht es mir nichts aus, den Ausdruck Route zu gehen, wenn es keine Antwort auf die gestellte Frage gibt , wie unten gezeigt:

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();
}

Oder bin ich nach dem Nichtexistenten (da ich so etwas noch nirgends gesehen habe)?

Akzeptierte Antwort

Der Feldzugriff wird nicht über eine Methode (wie Getter und Setter) ausgeführt - sie wird mit einer IL-Anweisung ausgeführt - es gibt also nichts, was Sie einem Delegaten zuweisen können. Sie müssen die Expression-Route verwenden, um einen "Block" Code (effektiv IL) zu erstellen, der einem Delegaten zugewiesen werden kann.


Beliebte Antwort

Wie Peter Ritchie vorgeschlagen hat, können Sie zur Laufzeit einen eigenen Code erstellen. Die Methode wird kompiliert, sobald Sie den Delegaten zum ersten Mal aufrufen. Der erste Aufruf wird also langsam sein, aber jeder nachfolgende Aufruf wird so schnell sein, wie Sie in .NET ohne unverwaltete Zeiger / Unionen erhalten können. Mit Ausnahme des ersten Aufrufs ist der Delegat etwa 500 mal schneller als FieldInfo.

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>));
    }
}

Beachten Sie, dass Strukturen nach Wert übergeben werden. Das bedeutet, dass eine Action<S, T> nicht zum Ändern von Mitgliedern einer Struktur verwendet werden kann, wenn sie als erstes Argument nach Wert übergeben wird.



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow