Il existe un groupe de méthodes privées dans ma classe et je dois en appeler une de manière dynamique en fonction d'une valeur d'entrée. Le code appelant et les méthodes cible se trouvent 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 renverra pas de méthodes privées. Quelle 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 d'énumération BindingFlags .
BindingFlags.NonPublic
ne retournera aucun résultat par lui-même. En fin de compte, le combiner avec BindingFlags.Instance
fait l'affaire.
MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType,
BindingFlags.NonPublic | BindingFlags.Instance);
Et si vous vraiment voulez vous créer des problèmes, facilitez-le à l’exécution en écrivant une méthode d’extension:
static class AccessExtensions
{
public static object call(this object o, string methodName, params object[] args)
{
var mi = o.GetType ().GetMethod (methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance );
if (mi != null) {
return mi.Invoke (o, args);
}
return null;
}
}
Et utilisation:
class Counter
{
public int count { get; private set; }
void incr(int value) { count += value; }
}
[Test]
public void making_questionable_life_choices()
{
Counter c = new Counter ();
c.call ("incr", 2); // "incr" is private !
c.call ("incr", 3);
Assert.AreEqual (5, c.count);
}
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 souhaitée est dans une superclasse de obj
, le T
generic doit être explicitement défini sur le type de la superclasse.
Si la méthode est asynchrone, vous pouvez utiliser await (Task) obj.InvokeMethod(…)
.
Êtes-vous absolument sûr que cela ne peut se faire par héritage? La réflexion est la dernière chose à examiner lors de la résolution d'un problème. Elle rend plus difficile la refactorisation, la compréhension de votre code et toute analyse automatisée.
Il semble que vous devriez simplement avoir une classe DrawItem1, DrawItem2, etc. qui remplace votre méthode dynMethod.
La réflexion des membres privés rompt encapsulation le principe et expose ainsi votre code aux éléments suivants:
Dans certains 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 ils ne veulent pas modifier l'interface pour donner accès aux membres internes uniquement pour des tests.
Pour atténuer ce problème, le mieux est de détecter toute rupture potentielle en effectuant des tests unitaires qui s'exécuteraient dans une version à intégration continue ou similaire. Bien sûr, cela signifie que vous utilisez toujours la même Assemblée (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.
Dans les versions récentes de .Net Framework, CreateDelegate a battu d'un facteur 50 l'invocation 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);
Les appels draw
seront environ 50 fois plus rapides que MethodInfo.Invoke
utilisez draw
en tant que Func
standard comme celui-ci:
var res = draw(methodParams);
Cochez cette poste de la mienne pour voir le point de repère sur différentes invocations de méthode
Ne pourriez-vous pas simplement avoir une méthode Draw différente pour chaque type que vous souhaitez dessiner? Appelez ensuite la méthode Draw surchargée en passant à l'objet de type itemType à dessiner.
Votre question n'indique pas clairement si itemType fait véritablement référence à des objets de types différents.
Je pense que vous pouvez le transmettre BindingFlags.NonPublic
où it est la méthode GetMethod
.
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);
}
BindingFlags.NonPublic
Lisez cette réponse (supplémentaire) (qui est parfois la réponse) pour comprendre où cela se passe et pourquoi certaines personnes dans ce fil se plaignent que "cela ne fonctionne toujours pas"
J'ai écrit exactement le même code que l'une des réponses ici . Mais j'avais toujours un problème. J'ai placé le point de rupture sur
var mi = o.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance );
Il a exécuté mais mi == null
Et cela a continué comme ça jusqu'à ce que je "reconstruise" tous les projets impliqués. J'étais en train de tester une assemblée alors que la méthode de réflexion reposait dans la troisième assemblée. C'était totalement déroutant, mais j'ai utilisé Immediate Window pour découvrir des méthodes et j'ai découvert qu'une méthode privée que j'avais essayée de tester à l'unité avait l'ancien nom (je l'ai renommée). Cela m'a permis de constater que l'ancien assemblage ou PDB existe toujours, même si le projet de test unitaire est construit - pour une raison quelconque, le projet que les tests n'ont pas construit. "reconstruire" a travaillé