¿Cómo uso la reflexión para invocar un método privado?

Hay un grupo de métodos privados en mi clase, y necesito llamar a uno de forma dinámica en función de un valor de entrada. Tanto el código de invocación como los métodos de destino están en la misma instancia. El código se ve así:

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

En este caso, GetMethod() no devolverá métodos privados. ¿Qué BindingFlags necesito suministrar a GetMethod() para que pueda ubicar métodos privados?

Simplemente cambie su código para usar la versión sobrecargada de GetMethod que acepta BindingFlags:

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

Aquí está la documentación de enumeración de BindingFlags .

BindingFlags.NonPublic no devolverá ningún resultado por sí mismo. Como resultado, combinándolo con BindingFlags.Instance hace el truco.

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

Y si realmente quiere meterse en problemas, haga que sea más fácil ejecutarlo escribiendo un método de extensión:

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

Y el uso:

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

¿Estás absolutamente seguro de que esto no puede hacerse a través de la herencia? La reflexión es lo último que debe considerar al resolver un problema, ya que dificulta la refacturación, la comprensión de su código y cualquier análisis automatizado.

Parece que solo debería tener una clase DrawItem1, DrawItem2, etc. que anule su dynMethod.

Microsoft modificó recientemente la API de reflexión, lo que hace que la mayoría de estas respuestas sean obsoletas. Lo siguiente debería funcionar en plataformas modernas (incluyendo Xamarin.Forms y UWP):

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

O como un método de extensión:

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

Nota:

  • Si el método deseado está en una superclase de obj el genérico T debe establecerse explícitamente según el tipo de la superclase.

  • Si el método es asincrónico puede usar await (Task) obj.InvokeMethod(…) .

La reflexión especialmente en los miembros privados es incorrecta

  • La reflexión rompe seguridad tipo. Puede intentar invocar un método que ya no existe (o más), o con los parámetros incorrectos, o con demasiados parámetros, o no lo suficiente … o incluso en el orden incorrecto (este es mi favorito :)). Por cierto, el tipo de devolución podría cambiar también.
  • La reflexión es lenta.

La reflexión de miembros privados rompe el principio de encapsulación y expone su código a lo siguiente:

  • Aumente la complejidad de su código porque tiene que manejar el comportamiento interno de las clases. Lo que está oculto debe permanecer oculto.
  • Hace que su código sea fácil de romper, ya que se comstackrá, pero no se ejecutará si el método cambió su nombre.
  • Hace que el código privado sea fácil de romper porque, si es privado, no debe llamarse así. Tal vez el método privado espera algún estado interno antes de ser llamado.

¿Qué pasa si debo hacerlo de todos modos?

Hay casos así, cuando dependes de un tercero o necesitas una API que no esté expuesta, tienes que reflexionar un poco. Algunos también lo usan para probar algunas clases que poseen, pero que no quieren cambiar la interfaz para dar acceso a los miembros internos solo para las pruebas.

Si lo haces, hazlo bien

  • Mitigar lo fácil de romper:

Para mitigar el problema fácil de romper, lo mejor es detectar cualquier ruptura potencial mediante pruebas en pruebas unitarias que se ejecutarían en una construcción de integración continua o similar. Por supuesto, significa que siempre usa el mismo ensamblaje (que contiene los miembros privados). Si usa una carga dinámica y reflexión, le gusta jugar con fuego, pero siempre puede atrapar la excepción que la llamada puede producir.

  • Mitigar la lentitud de la reflexión:

En las versiones recientes de .Net Framework, CreateDelegate venció por un factor 50 la invocación de 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)_method.CreateDelegate( typeof(Func), this); 

draw llamadas al draw serán alrededor de MethodInfo.Invoke más rápidas que las de MethodInfo.Invoke use draw como un Func estándar como ese:

 var res = draw(methodParams); 

Verifique este post mío para ver el punto de referencia en diferentes invocaciones de métodos

¿No podría tener un método de dibujo diferente para cada tipo que desee dibujar? Luego llame al método Draw sobrecargado que pasa en el objeto de tipo itemType a dibujar.

Su pregunta no aclara si itemType se refiere genuinamente a objetos de diferentes tipos.

Creo que puedes pasarlo BindingFlags.NonPublic donde está el método GetMethod .

BindingFlags.NonPublic

Invoca cualquier método a pesar de su nivel de protección en instancia de objeto. ¡Disfrutar!

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

Lea esta respuesta (suplementaria) (que a veces es la respuesta) para comprender a dónde va esto y por qué algunas personas en este hilo se quejan de que “todavía no está funcionando”.

Escribí exactamente el mismo código que una de las respuestas aquí . Pero todavía tenía un problema. Coloqué el punto de quiebre en

 var mi = o.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance ); 

Se ejecutó pero mi == null

Y continuó comportamientos como este hasta que “reconstruí” en todos los proyectos involucrados. Estaba probando un ensamblaje por unidad, mientras que el método de reflexión estaba sentado en el tercer ensamblaje. Fue totalmente confuso pero utilicé la ventana Inmediato para descubrir métodos y encontré que un método privado que traté de probar la unidad tenía un nombre antiguo (lo renombré). Esto me dijo que el ensamblaje antiguo o PDB todavía está ahí, incluso si el proyecto de prueba de unidad se construye, por alguna razón el proyecto no se construyó. “reconstruir” funcionó