C'è un modo per creare un delegato per ottenere e impostare valori per un FieldInfo?

c# delegates expression-trees fieldinfo setvalue

Domanda

Per le proprietà ci sono GetGetMethod e GetSetMethod modo che io possa fare:

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

e

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

Ma come faccio a sapere di FieldInfo ?

Non sto cercando delegati su GetValue e SetValue (il che significa che invocherò ogni volta la riflessione)

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

ma se c'è un approccio CreateDelegate qui? Voglio dire dal momento che gli incarichi restituiscono un valore , posso trattare i compiti come un metodo? Se è così c'è un handle MethodInfo per questo? In altre parole, come faccio a passare il giusto MethodInfo di impostazione e ottenere un valore da un campo membro al metodo CreateDelegate modo da ottenere un delegato indietro con cui posso leggere e scrivere direttamente nei campi?

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

Posso creare espressioni e compilarle, ma sto cercando qualcosa di più semplice. Alla fine non mi interessa andare sul percorso dell'espressione se non c'è una risposta per la domanda , come mostrato di seguito:

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

O sono dopo l'inesistente (dal momento che non ho visto nulla di simile ancora oggi)?

Risposta accettata

L'accesso al campo non viene eseguito tramite un metodo (come getter e setter): viene eseguito con un'istruzione IL, quindi non c'è nulla che tu possa assegnare a un delegato. dovrai utilizzare il percorso dell'espressione per creare un "blocco" di codice (effettivamente IL) che può essere assegnato a un delegato.


Risposta popolare

Come suggerito da Peter Ritchie, è possibile compilare il proprio codice in fase di runtime. Il metodo verrà compilato non appena si invoca il delegato per la prima volta. Quindi la prima chiamata sarà lenta, ma ogni chiamata successiva sarà veloce quanto è possibile ottenere in .NET senza puntatori / unioni non gestiti. Tranne che per la prima chiamata, il delegato è circa 500 volte più veloce di FieldInfo direttamente.

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

Tieni presente che le strutture vengono passate per valore. Ciò significa che l' Action<S, T> non può essere utilizzata per cambiare i membri di una struct se viene passata per valore come primo argomento.



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché