La reflexión ( reflexión ; un holónimo de introspección , reflexión en inglés ) es un proceso durante el cual un programa puede rastrear y modificar su propia estructura y comportamiento en tiempo de ejecución. El paradigma de programación subyacente a la reflexión es una de las formas de metaprogramación [1] y se denomina programación reflexiva .
Durante la ejecución de las instrucciones del programa (código), las computadoras procesan los datos, lo que conduce a su cambio, mientras que las computadoras no modifican el código. Sin embargo, en la mayoría de las arquitecturas informáticas modernas , el código se almacena como datos, y algunos lenguajes de programación tienen la capacidad de tratar el código nativo como datos, lo que provoca cambios en el propio código durante su ejecución. Dichos programas que se modifican a sí mismos se crean principalmente con lenguajes de programación de alto nivel que utilizan máquinas virtuales (por ejemplo , Smalltalk , lenguajes de secuencias de comandos ). En menor medida, la reflexión se usa en lenguajes con tipos declarados o estáticos (por ejemplo , C , ML , Haskell , F# ).
El concepto de reflexión en lenguajes de programación fue introducido por Brian Cantwell Smith en su tesis doctoral de 1982 [ 2] [3] junto con el concepto de un evaluador meta -circular como componente de 3-Lisp .
La programación orientada reflexiva, o programación reflexiva, es una extensión funcional del paradigma de programación orientada a objetos . La programación orientada a la reflexión incluye la autocomprobación, la automodificación y la autoclonación. Sin embargo, la principal ventaja del paradigma orientado a la reflexión radica en la modificación dinámica del programa, que se puede definir y ejecutar mientras el programa se está ejecutando. Algunos enfoques imperativos , como los paradigmas de programación orientada a objetos y procedimentales, indican que existe una secuencia predefinida clara de operaciones de procesamiento de datos. Sin embargo, el paradigma de programación orientado a la reflexión agrega la capacidad de modificar dinámicamente las instrucciones del programa en tiempo de ejecución y llamarlas en una forma modificada. Es decir, la propia arquitectura del software determina qué se puede hacer exactamente durante la operación en función de los datos, los servicios y las operaciones específicas.
La reflexión se puede utilizar para observar y modificar un programa durante la ejecución. El componente reflexivo del programa puede observar la ejecución de una determinada pieza de código y cambiarse a sí mismo para lograr el objetivo deseado. La modificación se realiza durante la ejecución del programa cambiando dinámicamente el código.
La reflexión también se puede utilizar para adaptar dinámicamente un programa a diferentes situaciones. Por ejemplo, considere un programa que usa dos clases diferentes Xy Ypara realizar operaciones similares. Sin reflejo en el código del programa, los métodos de clase Xse Yllamarán explícitamente. Si el programa está diseñado utilizando el paradigma de programación orientado a la reflexión, alguna sección del código no contendrá llamadas explícitas a métodos de clase Xy Y; el programa ejecutará esta sección dos veces: primero para la clase X, luego para la clase Y.
Un ejemplo que ilustra los beneficios de la reflexión es la serialización de un objeto a JSON . Sin reflexión, sería necesario especificar explícitamente todos los nombres de campo de clase y hacer referencia a sus valores para la serialización. Pero la reflexión permite que el propio programa determine todos los campos disponibles y obtenga sus nombres textuales. Por lo tanto, la serialización está disponible para cualquier objeto sin necesidad de escribir código adicional.
Los programas escritos en lenguajes de programación que admiten la reflexión están dotados de características adicionales que son difíciles de implementar en lenguajes de bajo nivel. Enumeramos algunos de ellos:
Estas características se pueden implementar de diferentes maneras. En el lenguaje MOO , la reflexión es parte del lenguaje de programación diario. Todos los métodos llamados reciben en el contexto información sobre desde dónde son llamados y referencias a los objetos a los que pertenecen. La seguridad se controla mediante programación mediante la pila de llamadas: se llama a los llamadores() para obtener una lista de métodos; comprueba si las personas que llaman()[1] se han bloqueado.
Los lenguajes compilados se basan en sus entornos de tiempo de ejecución para proporcionar a los programas información sobre su código fuente. Un archivo ejecutable compilado en Objective-C , por ejemplo, escribe los nombres de todos los métodos en un bloque, crea una tabla de búsqueda. En lenguajes compilados que admiten la creación de funciones en tiempo de ejecución, como Common Lisp , el tiempo de ejecución debe incluir un compilador y un intérprete.
La implementación de la reflexión en lenguajes que no la admiten se realiza utilizando el sistema de transformación del programa para rastrear automáticamente los cambios en el código fuente.
Un ejemplo en C# , en el que se crea una instancia foode la clase Fooy se hace una llamada a un método Helloque no usa reflejo y lo usa:
// Sin reflejo nuevo Foo (). hola (); // Con reflexión Tipo type = System . tipo _ GetType ( "foo" ); var foo = Activador . CreateInstance ( tipo ); foo _ Gettype (). GetMethod ( "Hola" ). Invocar ( foo , null );Ejemplo similar para ECMAScript , JavaScript y ActionScript :
// Sin reflejo nuevo Foo (). hola () // Con reflejo // asumiendo que Foo está en este nuevo this [ 'Foo' ]()[ 'hello' ]() // sin suposiciones new ( eval ( 'Foo' ))()[ 'hola' ]()