¿Hay problemas con el uso de Reflection?

49

No sé por qué, pero siempre siento que estoy "haciendo trampa" cuando uso la reflexión. Tal vez sea por el impacto en el rendimiento que sé que estoy tomando.

Una parte de mí dice, si es parte del lenguaje que está utilizando y puede lograr lo que está tratando de hacer, entonces ¿por qué no usarlo? La otra parte de mí dice, tiene que haber una manera de hacerlo sin usar la reflexión. Supongo que tal vez depende de la situación.

¿Cuáles son los problemas potenciales que debo tener en cuenta al usar la reflexión y qué tan preocupado debo estar con ellos? ¿Cuánto esfuerzo vale la pena gastar para tratar de encontrar una solución más convencional?

    
pregunta P B 15.08.2011 - 16:11

14 respuestas

47

No, no es hacer trampa, es una forma de resolver problemas en algunos lenguajes de programación.

Ahora, a menudo no es la mejor solución (la más limpia, la más sencilla y la más fácil de mantener). Si hay una mejor manera, use esa de hecho. Sin embargo, a veces no hay. O si lo hay, es mucho más complejo, lo que implica una gran cantidad de duplicación de código, etc., lo que lo hace inviable (difícil de mantener a largo plazo).

Dos ejemplos de nuestro proyecto actual (Java):

  • algunas de nuestras herramientas de prueba utilizan la reflexión para cargar la configuración desde archivos XML. La clase que se inicializará tiene campos específicos, y el cargador de configuración usa la reflexión para hacer coincidir el elemento XML llamado fieldX con el campo apropiado en la clase, y para inicializar este último. En algunos casos, puede crear un cuadro de diálogo simple de la GUI a partir de las propiedades identificadas sobre la marcha. Sin reflexión, esto llevaría cientos de líneas de código a través de varias aplicaciones. Por lo tanto, la reflexión nos ayudó a armar una herramienta simple rápidamente, sin mucha molestia, y nos permitió centrarnos en la parte importante (pruebas de regresión de nuestra aplicación web, análisis de registros del servidor, etc.) en lugar de lo irrelevante.
  • un módulo de nuestra aplicación web heredada estaba destinado a exportar / importar datos desde tablas DB a hojas de Excel y viceversa. Contenía una gran cantidad de código duplicado, donde, por supuesto, las duplicaciones no eran exactamente iguales, algunas contenían errores, etc. Usando la reflexión, la introspección y las anotaciones, logré eliminar la mayor parte de la duplicación, reduciendo la cantidad de código de más de De 5 K a menos de 2,4 K, al tiempo que hace que el código sea más robusto y mucho más fácil de mantener o extender. Ahora ese módulo dejó de ser un problema para nosotros, gracias al uso juicioso de la reflexión.

La línea de fondo es, como cualquier herramienta poderosa, la reflexión también puede usarse para dispararte en el pie. Si aprende cuándo y cómo (no) usarlo, puede brindarle soluciones elegantes y limpias para los problemas difíciles. Si abusas de él, puedes convertir un problema simple en un desorden complejo y feo.

    
respondido por el Péter Török 15.08.2011 - 16:20
34

No es hacer trampa. Pero generalmente es una mala idea en el código de producción por lo menos por las siguientes razones:

  • Se pierde la seguridad del tipo de tiempo de compilación : es útil que el compilador verifique que un método esté disponible en el momento de la compilación. Si está utilizando la reflexión, obtendrá un error en el tiempo de ejecución que podría afectar a los usuarios finales si no realiza las pruebas lo suficientemente bien. Incluso si detecta el error, será más difícil de depurar.
  • Causa errores al refactorizar : si está accediendo a un miembro según su nombre (p. ej., utilizando una cadena codificada), la mayoría de las herramientas de refactorización de código no lo cambiarán. un error, que puede ser bastante difícil de localizar.
  • El rendimiento es más lento : la reflexión en el tiempo de ejecución será más lenta que las búsquedas de métodos / búsquedas de métodos estáticamente compiladas. Si solo está haciendo reflexiones de vez en cuando, entonces no importará, pero esto puede convertirse en un cuello de botella en el rendimiento en los casos en que realiza llamadas a través de la reflexión miles o millones de veces por segundo. Una vez obtuve una aceleración de 10x en algún código de Clojure simplemente eliminando toda reflexión, así que sí, este es un problema real.

Sugeriría limitar el uso de la reflexión a los siguientes casos:

  • Para el código de creación rápida de prototipos o "desechable" cuando es la solución más simple
  • Para casos de uso de reflexión genuinos , por ejemplo, Herramientas IDE que permiten a un usuario examinar los campos / métodos de un objeto arbitrario en tiempo de ejecución.

En todos los demás casos, sugeriría descubrir un enfoque que evite la reflexión. Definir una interfaz con el (los) método (s) apropiado (s) e implementarlo en el conjunto de clases a las que desea llamar el método (s) es generalmente suficiente para resolver los casos más simples.

    
respondido por el mikera 15.08.2011 - 17:29
11

La reflexión es solo otra forma de meta-programación, y tan válida, como los parámetros basados en tipos que se ven en la mayoría de los idiomas en estos días. La reflexión es poderosa y genérica, y los programas reflexivos son un alto orden de mantenimiento (cuando se usan correctamente, por supuesto) y más que los programas puramente orientados a objetos o procedimientos. Sí, pagas un precio de rendimiento, pero me encantaría ir a un programa más lento que sea más fácil de mantener en muchos, o incluso en la mayoría de los casos.

    
respondido por el DeadMG 15.08.2011 - 16:21
8

Seguramente todo depende de lo que intentas lograr.

Por ejemplo, escribí una aplicación de comprobación de medios que utiliza la inyección de dependencias para determinar qué tipo de medios (archivos MP3 o JPEG) se deben verificar. El shell necesitaba mostrar una cuadrícula con la información pertinente para cada tipo, pero no tenía conocimiento de qué se iba a mostrar. Esto se define en el ensamblaje que lee ese tipo de medio.

Por lo tanto, tuve que usar la reflexión para obtener el número de columnas que se mostrarían y sus tipos y nombres para poder configurar la cuadrícula correctamente. También significaba que podía actualizar la biblioteca inyectada (o crear una nueva) sin cambiar ningún otro código o archivo de configuración.

La única otra forma habría sido tener un archivo de configuración que necesitaría actualizarse cuando cambié el tipo de medio que se está verificando. Esto hubiera introducido otro punto de falla para la aplicación.

    
respondido por el ChrisF 15.08.2011 - 16:17
7

Reflection es una herramienta increíble si usted es un autor de library y, por lo tanto, no tiene influencia sobre los datos entrantes. Una combinación de reflexión y metaprogramación puede permitir que su biblioteca funcione sin problemas con llamadores arbitrarios, sin que tengan que saltar a través de aros de generación de códigos, etc.

Sin embargo, trato de desalentar la reflexión en el código de aplicación ; en la capa de la aplicación, deberías usar diferentes metáforas: interfaces, abstracción, encapsulación, etc.

    
respondido por el Marc Gravell 15.08.2011 - 20:43
6

Reflection es fantástico para crear herramientas para desarrolladores.

Ya que le permite a su entorno de compilación inspeccionar el código y potencialmente generar las herramientas correctas para manipular / iniciar la inspección del código.

Como técnica de programación general, puede ser útil pero es más frágil de lo que la mayoría de la gente imagina.

Un uso realmente de desarrollo para la reflexión (IMO) es que hace que la escritura de una biblioteca de transmisión genérica sea muy simple (siempre que la descripción de su clase nunca cambie (entonces se convierta en una solución muy frágil)).

    
respondido por el Martin York 15.08.2011 - 16:45
5

El uso de la reflexión suele ser perjudicial en los idiomas OO si no se utiliza con gran conciencia.

He perdido la cuenta de cuántas preguntas malas he visto en los sitios de StackExchange donde

  1. El idioma en uso es OO
  2. El autor desea averiguar qué tipo de objeto se ha pasado y luego llamar a uno de sus métodos
  3. El autor se niega a aceptar que la reflexión es una técnica incorrecta y acusa a cualquiera que señale que "no sabe cómo ayudarme"

Aquí son ejemplos típicos.

La mayor parte del punto de OO es que

  1. Agrupas funciones que saben cómo manipular una estructura / concepto con la cosa en sí misma.
  2. En cualquier punto de su código, debe saber lo suficiente sobre el tipo de un objeto para saber qué funciones relevantes tiene y pedirle que llame a su versión particular de esas funciones.
  3. Usted asegura la verdad del punto anterior especificando el tipo de entrada correcto para cada función y haciendo que cada función no sepa más (y no haga más) lo que sea relevante.

Si en algún punto de su código, el punto 2 no es válido para un objeto que ha pasado, entonces uno o más de estos son verdaderos

  1. Se está introduciendo la entrada incorrecta
  2. Su código está mal estructurado
  3. No tienes idea de qué demonios estás haciendo.

Los desarrolladores poco cualificados simplemente no entienden esto y creen que se les puede pasar cualquier cosa en cualquier parte de su código y hacen lo que quieren de un conjunto de posibilidades (codificado). Estos idiotas usan la reflexión mucho .

Para los idiomas OO, la reflexión solo debe ser necesaria en la meta actividad (cargadores de clases, inyección de dependencia, etc.). En esos contextos, la reflexión es necesaria porque está proporcionando un servicio genérico para ayudar en la manipulación / configuración del código del cual no sabe nada por una razón válida y legítima. En casi cualquier otra situación, si está tratando de reflexionar, entonces está haciendo algo mal y necesita preguntarse por qué esta pieza de código no sabe lo suficiente sobre el objeto que se le ha pasado.

    
respondido por el itsbruce 29.01.2015 - 18:59
3

Una alternativa, en los casos en que el dominio de las clases reflejadas está bien definido, es usar la reflexión junto con otros metadatos para generar código , en lugar de usar la reflexión en tiempo de ejecución. Hago esto usando FreeMarker / FMPP; Hay muchas otras herramientas para elegir. La ventaja de esto es que terminas con un código "real" que puede ser fácilmente depurado, etc.

Dependiendo de la situación, esto puede ayudarte a hacer un código mucho más rápido, o simplemente un montón de código inflado. Evita las desventajas de la reflexión:

  • pérdida de seguridad de tipo de tiempo de compilación
  • errores debidos a la refactorización
  • rendimiento más lento

mencionado anteriormente.

Si la reflexión tiene ganas de hacer trampa, puede deberse a que no está seguro de basarse en una gran cantidad de conjeturas y su instinto le advierte que esto es riesgoso. Asegúrese de proporcionar una manera de mejorar los metadatos inherentes a la reflexión con sus propios metadatos, donde puede describir todas las peculiaridades y casos especiales de las clases del mundo real que pueda encontrar.

    
respondido por el Ed Staub 16.08.2011 - 02:19
2

No es hacer trampa, pero como cualquier herramienta, debe usarse para lo que pretende resolver. La reflexión, por definición, le permite inspeccionar y modificar el código a través del código; Si eso es lo que necesita hacer, entonces la reflexión es la herramienta para el trabajo. La reflexión tiene que ver con el meta-código: código que apunta al código (a diferencia del código regular, que apunta a los datos).

Un ejemplo de buen uso de la reflexión son las clases de interfaz de servicio web genéricas: un diseño típico es separar la implementación del protocolo de la funcionalidad de carga útil. Entonces, tienes una clase (llamémosla T ) que implementa tu carga útil, y otra que implementa el protocolo ( P ). T es bastante sencillo: para cada llamada que desee realizar, simplemente escriba un método que haga lo que se supone que debe hacer. P , sin embargo, necesita asignar las llamadas de servicio web a las llamadas de método. Hacer este mapeo genérico es deseable, ya que evita la redundancia y hace que P sea altamente reutilizable. Reflexión proporciona los medios para inspeccionar la clase T en tiempo de ejecución y llamar a sus métodos basados en cadenas pasadas a P a través del protocolo de servicio web, sin ningún conocimiento en tiempo de compilación de la clase T . Usando la regla de 'código sobre código', se puede argumentar que la clase P tiene el código en la clase T como parte de sus datos.

Sin embargo.

Reflection también le brinda herramientas para sortear las restricciones del sistema tipográfico del idioma; teóricamente, puede pasar todos los parámetros como tipo object y llamar a sus métodos a través de reflexiones. Voilà, el lenguaje que se supone que impone una fuerte disciplina de escritura estática ahora se comporta como un lenguaje tipificado dinámicamente con un enlace tardío, solo que la sintaxis es mucho más elaborada. Cada instancia de tal patrón que he visto hasta ahora ha sido un truco sucio, e invariablemente, una solución dentro del sistema tipográfico del lenguaje hubiera sido posible, y habría sido más segura, más elegante y más eficiente en todos los aspectos. .

Existen algunas excepciones, como los controles GUI que pueden vincularse a varios tipos de fuentes de datos no relacionados; exigir que sus datos implementen una interfaz determinada solo para que pueda enlazarlos, no es realista, y el programador no implementa un adaptador para cada tipo de fuente de datos. En este caso, usar la reflexión para detectar el tipo de origen de datos y ajustar el enlace de datos es una opción más útil.

    
respondido por el tdammers 15.08.2011 - 23:27
2

Uno de los problemas que hemos tenido con Reflection es cuando agregamos Ofuscación a la combinación. Todas las clases obtienen nombres nuevos y, de repente, la carga de una clase o función por su nombre deja de funcionar.

    
respondido por el Carra 16.08.2011 - 09:49
1

Depende totalmente. Un ejemplo de algo que sería difícil de hacer sin reflexión sería replicar ObjectListView . También genera código IL sobre la marcha.

    
respondido por el Tangurena 15.08.2011 - 16:32
1

La reflexión es el método principal para crear sistemas basados en convenciones. No me sorprendería descubrir que está en uso en la mayoría de los marcos MVC. Es un componente importante en ORMs. Lo más probable es que ya esté utilizando componentes creados todos los días.

La alternativa para tal uso es la configuración, que tiene su propio conjunto de inconvenientes.

    
respondido por el Chris McCall 29.01.2015 - 21:31
0

La reflexión puede lograr cosas que simplemente no se pueden hacer prácticamente de otro modo.

Por ejemplo, considera cómo optimizar este código:

int PoorHash(char Operator, int seed, IEnumerable<int> values) {
    foreach (var v in values) {
        seed += 1;
        switch (char) {
            case '+': seed += v; break;
            case '^': seed ^= v; break;
            case '-': seed -= v; break;
            ...
        }
        seed *= 3;
    }
    return seed;
}

Hay un costoso golpe de prueba en el medio del bucle interno, pero extraerlo requiere volver a escribir el bucle una vez para cada operador. La reflexión nos permite obtener un rendimiento a la par con la extracción de esa prueba, sin repetir el ciclo una docena de veces (y, por lo tanto, sacrificar la capacidad de mantenimiento). Simplemente genere y compile el bucle que necesita sobre la marcha.

Tengo en realidad he realizado esta optimización del / el / el / la / el / la / el / la / el / las / las / las / las Un poco más complicado de una situación, y los resultados fueron sorprendentes. Una mejora de orden de magnitud en el rendimiento y menos líneas de código.

(Nota: inicialmente intenté el equivalente a pasar un Func en lugar de un char, y fue un poco mejor, pero no casi la reflexión de 10x lograda).

    
respondido por el Craig Gidney 15.08.2011 - 18:22
-2

De ninguna manera es hacer trampa ... Más bien, faculta a los servidores de aplicaciones para ejecutar las clases creadas por el usuario con el nombre que elijan, por lo tanto, brindan flexibilidad al usuario, no las engañan.

¡Y si desea ver el código de cualquier archivo .class (en Java), hay varios descompiladores disponibles de forma gratuita!

    
respondido por el ANSHUL JAIN 15.08.2011 - 18:05

Lea otras preguntas en las etiquetas