Es gibt eine Gruppe privater Methoden in meiner Klasse, die ich dynamisch basierend auf einem Eingabewert aufrufen muss. Sowohl der aufrufende Code als auch die Zielmethode befinden sich in derselben Instanz. Der Code sieht so aus:
MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType);
dynMethod.Invoke(this, new object[] { methodParams });
In diesem Fall gibt GetMethod()
keine privaten Methoden zurück. Welche BindingFlags
muss ich an GetMethod()
angeben, damit private Methoden gefunden werden können?
Ändern Sie einfach Ihren Code, um die überladene Version von GetMethod
zu verwenden, die BindingFlags akzeptiert:
MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType,
BindingFlags.NonPublic | BindingFlags.Instance);
dynMethod.Invoke(this, new object[] { methodParams });
Hier ist die BindingFlags-Enumeration-Dokumentation .
BindingFlags.NonPublic
gibt selbst keine Ergebnisse zurück. Die Kombination mit BindingFlags.Instance
führt den Trick aus.
MethodInfo dynMethod = this.GetType().GetMethod("Draw_" + itemType,
BindingFlags.NonPublic | BindingFlags.Instance);
Und wenn Sie wirklich sich selbst in Schwierigkeiten bringen möchten, erleichtern Sie die Ausführung, indem Sie eine Erweiterungsmethode schreiben:
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;
}
}
Und Verwendung:
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 hat vor kurzem die Reflection-API geändert die meisten dieser Antworten sind veraltet. Folgendes sollte auf modernen Plattformen (einschließlich Xamarin.Forms und UWP) funktionieren:
obj.GetType().GetTypeInfo().GetDeclaredMethod("MethodName").Invoke(obj, yourArgsHere);
Oder als Erweiterungsmethode:
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);
}
Hinweis:
Wenn sich die gewünschte Methode in einer Oberklasse von obj
befindet, muss der Generic T
explizit auf den Typ der Oberklasse gesetzt werden.
Wenn die Methode asynchron ist, können Sie await (Task) obj.InvokeMethod(…)
verwenden.
Sind Sie absolut sicher, dass dies nicht durch Vererbung erfolgen kann? Überlegungen sind das letzte, was Sie bei der Lösung eines Problems beachten sollten. Dies macht das Refactoring, das Verstehen Ihres Codes und jede automatisierte Analyse schwieriger.
Anscheinend sollten Sie nur eine DrawItem1-, DrawItem2 usw.-Klasse haben, die Ihre dynMethod überschreibt.
Private Members Reflection Breaks Encapsulation Prinzip und macht Ihren Code daher den folgenden Informationen zugänglich:
Es gibt Fälle, in denen Sie von einer dritten Partei abhängig sind oder ein nicht offengelegtes API benötigen, müssen Sie darüber nachdenken. Einige verwenden es auch, um einige Klassen zu testen, die sie besitzen, aber sie möchten die Schnittstelle nicht ändern, um nur für Tests Zugriff auf die inneren Mitglieder zu erhalten.
Um das Problem zu lösen, das leicht zu brechen ist, sollten Sie potenzielle Brüche am besten durch Testen von Komponententests erkennen, die in einem kontinuierlichen Integrations-Build oder ähnlichem ausgeführt werden. Natürlich bedeutet dies, dass Sie immer dieselbe Assembly (die die privaten Mitglieder enthält) verwenden. Wenn Sie eine dynamische Last und Reflexion verwenden, spielen Sie gerne mit dem Feuer, aber Sie können immer die Ausnahme erkennen, die der Anruf erzeugt.
In den aktuellen Versionen von .Net Framework schlägt CreateDelegate um den Faktor 50, den MethodInfo aufruft:
// 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
-Aufrufe sind etwa 50-mal schneller als MethodInfo.Invoke
Verwenden Sie draw
als Standard Func
wie folgt:
var res = draw(methodParams);
Überprüfen Sie diese post von mir , um Benchmark für verschiedene Methodenaufrufe zu sehen
Könnten Sie nicht für jeden Typ, den Sie zeichnen möchten, eine andere Draw-Methode verwenden? Rufen Sie dann die überladene Draw-Methode auf, indem Sie das zu zeichnende Objekt vom Typ itemType übergeben.
Ihre Frage macht nicht klar, ob itemType wirklich auf Objekte unterschiedlichen Typs verweist.
Ich denke, Sie können es BindingFlags.NonPublic
übergeben, wobei it die GetMethod
-Methode ist.
Ruft eine beliebige Methode trotz ihrer Schutzstufe für die Objektinstanz auf. Genießen!
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
Lies diese (ergänzende) Antwort (das ist manchmal die Antwort), um zu verstehen, wohin das geht und warum einige Leute in diesem Thread beschweren sich, dass "es funktioniert immer noch nicht"
Ich habe genau den gleichen Code wie eine der Antworten hier geschrieben . Aber ich hatte immer noch ein Problem. Ich habe einen Haltepunkt gesetzt
var mi = o.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance );
Es wurde ausgeführt, aber mi == null
Und das Verhalten hielt an, bis ich alle beteiligten Projekte "neu aufgebaut" habe. Ich habe gerade eine Baugruppe getestet, während die Reflexionsmethode in der dritten Baugruppe saß. Es war total verwirrend, aber ich verwendete sofortiges Fenster, um Methoden zu entdecken, und ich fand heraus, dass eine private Methode, die ich versuchte, Tests zu machen, einen alten Namen hatte (ich habe ihn umbenannt). Dies sagte mir, dass alte Assembly oder PDB immer noch da draußen sind, auch wenn ein Unit-Test-Projekt erstellt wird - aus irgendeinem Grund wurden die Tests nicht gebaut. "Wiederaufbau" hat funktioniert