être - reflexion c# example




Comment utiliser la réflexion pour invoquer une méthode privée? (7)

La réflexion surtout sur les membres privés est mauvaise

  • La réflexion brise la sécurité de type. Vous pouvez essayer d'invoquer une méthode qui n'existe plus (ou plus), ou avec les mauvais paramètres, ou avec trop de paramètres, ou pas assez ... ou même dans le mauvais ordre (celui-ci mon préféré :)). Par ailleurs, le type de retour pourrait également changer.
  • La réflexion est lente.

La réflexion des membres privés rompt le principe d' encapsulation et expose ainsi votre code à ce qui suit:

  • Augmentez la complexité de votre code car il doit gérer le comportement interne des classes. Ce qui est caché doit rester caché.
  • Rend votre code facile à casser car il compilera mais ne fonctionnera pas si la méthode a changé son nom.
  • Rend le code privé facile à rompre parce que s'il est privé, il n'est pas destiné à être appelé de cette façon. Peut-être que la méthode privée attend un état interne avant d'être appelée.

Et si je dois le faire quand même?

Il y a donc des cas, lorsque vous dépendez d'un tiers ou que vous avez besoin d'une API non exposée, vous devez réfléchir. Certains l'utilisent également pour tester certaines classes qu'ils possèdent mais qu'ils ne veulent pas changer d'interface pour donner accès aux membres internes juste pour les tests.

Si vous le faites, faites-le bien

  • Atténuer le facile à casser:

Pour atténuer le problème facile à résoudre, le mieux est de détecter toute rupture potentielle en testant dans des tests unitaires qui seraient exécutés dans une construction d'intégration continue ou similaire. Bien sûr, cela signifie que vous utilisez toujours le même assemblage (qui contient les membres privés). Si vous utilisez une charge dynamique et une réflexion, vous aimez jouer avec le feu, mais vous pouvez toujours attraper l'exception que l'appel peut produire.

  • Atténuer la lenteur de la réflexion:

Dans les versions récentes de .Net Framework, CreateDelegate a un facteur de 50 par rapport à l'appel MethodInfo:

// The following should be done once since this does some reflection
var method = this.GetType().GetMethod("Draw_" + itemType, 
  BindingFlags.NonPublic | BindingFlags.Instance);

// Here we create a Func that targets the instance of type which has the 
// Draw_ItemType method
var draw = (Func<TInput, Output[]>)_method.CreateDelegate(
                 typeof(Func<TInput, TOutput[]>), this);

draw appels sera environ 50 fois plus rapide que l'utilisation de MethodInfo.Invoke draw comme un Func standard comme ça:

var res = draw(methodParams);

Cochez ce post pour voir le benchmark sur différentes invocations de méthode

Il y a un groupe de méthodes privées dans ma classe, et j'ai besoin d'en appeler une dynamiquement en fonction d'une valeur d'entrée. Le code appelant et les méthodes cibles sont tous les deux dans la même instance. Le code ressemble à ceci:

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType);
dynMethod.Invoke(this, new object[] { methodParams });

Dans ce cas, GetMethod() ne retournera pas les méthodes privées. Quel BindingFlags dois-je fournir à GetMethod() afin qu'il puisse localiser des méthodes privées?


Changez simplement votre code pour utiliser la version surchargée de GetMethod qui accepte BindingFlags:

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, 
    BindingFlags.NonPublic | BindingFlags.Instance);
dynMethod.Invoke(this, new object[] { methodParams });

Voici la documentation de l'énumération BindingFlags .


Etes-vous absolument sûr que cela ne peut pas être fait par héritage? La réflexion est la toute dernière chose que vous devriez regarder quand vous résolvez un problème, cela rend le refactoring, la compréhension de votre code, et toute analyse automatisée plus difficile.

Il semble que vous devriez juste avoir une classe DrawItem1, DrawItem2, etc qui remplace votre dynMethod.


Invoque n'importe quelle méthode malgré son niveau de protection sur l'instance d'objet. Prendre plaisir!

public static object InvokeMethod(object obj, string methodName, params object[] methodParams)
{
    var methodParamTypes = methodParams?.Select(p => p.GetType()).ToArray() ?? new Type[] { };
    var bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static;
    MethodInfo method = null;
    var type = obj.GetType();
    while (method == null && type != null)
    {
        method = type.GetMethod(methodName, bindingFlags, Type.DefaultBinder, methodParamTypes, null);
        type = type.BaseType;
    }

    return method?.Invoke(obj, methodParams);
}

Microsoft a récemment modifié l'API de réflexion rendant la plupart de ces réponses obsolètes. Ce qui suit devrait fonctionner sur les plates-formes modernes (y compris Xamarin.Forms et UWP):

obj.GetType().GetTypeInfo().GetDeclaredMethod("MethodName").Invoke(obj, yourArgsHere);

Ou comme méthode d'extension:

public static object InvokeMethod<T>(this T obj, string methodName, params object[] args)
{
    var type = typeof(T);
    var method = type.GetTypeInfo().GetDeclaredMethod(methodName);
    return method.Invoke(obj, args);
}

Remarque:

  • Si la méthode désirée est dans une superclasse d' obj le générique T doit être explicitement défini sur le type de la superclasse.

  • Si la méthode est asynchrone, vous pouvez utiliser await (Task) obj.InvokeMethod(…) .


Ne pourriez-vous pas avoir une méthode Draw différente pour chaque type que vous voulez dessiner? Appelez ensuite la méthode Draw surchargée passant dans l'objet de type itemType à dessiner.

Votre question ne précise pas si itemType fait vraiment référence à des objets de types différents.


BindingFlags.NonPublic ne renverra aucun résultat par lui-même. Comme il s'avère, la combinaison avec BindingFlags.Instance fait l'affaire.

MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType, 
    BindingFlags.NonPublic | BindingFlags.Instance);




reflection