Existe-t-il un moyen de créer un délégué pour obtenir et définir les valeurs d'un FieldInfo?

c# delegates expression-trees fieldinfo setvalue

Question

Pour les propriétés, il y a GetGetMethod et GetSetMethod afin que je puisse faire:

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

et

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

Mais comment je fais avec FieldInfo ?

Je ne cherche pas de délégués pour GetValue et SetValue (ce qui signifie que je vais invoquer une réflexion à chaque fois)

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

mais s'il y a une approche CreateDelegate ici? Je veux dire, puisque les affectations renvoient une valeur , puis-je traiter les affectations comme une méthode? Si tel est le cas, existe-t-il un MethodInfo ? En d'autres termes, comment puis-je passer la bonne MethodInfo pour définir et obtenir une valeur d'un champ membre à la méthode CreateDelegate afin d'obtenir un délégué avec lequel je peux lire et écrire directement dans les champs?

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

Je peux construire une expression et la compiler, mais je cherche quelque chose de plus simple. En fin de compte, cela ne me dérange pas de choisir la voie d'expression s'il n'y a pas de réponse à la question posée , comme indiqué ci-dessous:

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

Ou suis-je après l'inexistant (puisque je n'ai encore jamais vu quelque chose comme ça)?

Réponse acceptée

L'accès au champ n'est pas effectué via une méthode (comme les accesseurs et les setters), mais avec une instruction IL. Vous ne pouvez donc rien affecter à un délégué. vous devrez utiliser la route d'expression pour créer un "bloc" de code (en réalité IL) pouvant être affecté à un délégué.


Réponse populaire

Comme Peter Ritchie l'a suggéré, vous pouvez compiler votre propre code au moment de l'exécution. La méthode sera compilée dès que vous appelez le délégué pour la première fois. Le premier appel sera donc lent, mais tout appel suivant sera aussi rapide que vous pouvez entrer en .NET sans pointeurs / unions non gérés. À l'exception du premier appel, le délégué est environ 500 fois plus rapide que FieldInfo directement.

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

Gardez à l'esprit que les structs sont passés par valeur. Cela signifie qu'une Action<S, T> ne peut pas être utilisée pour changer les membres d'une structure si elle est passée par valeur comme premier argument.




Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi