La librería reflection de .NET nos va a permitir acceder a la información de los atributos que hemos definido en el artículo anterior.
Reflection es el nombre dado por microsoft al conjunto de utilidades que permite leer la información y metainformación de las dll y ejecutables de .NET en tiempo de ejecución. Su utilidad fundamental está contenida en la biblioteca System.Reflection y contiene diversas funciones para cargar ensamblados, crear objetos, invocar métodos o leer información, todo ello en tiempo de ejecución.
Este artículo se centrará unicamente en el uso de .NET para leer la información de atributos, aunque se hará uso de otras funciones para acceder a las clases o los métodos involucrados. Para más información os recomiendo que leáis el artículo sobre .NET Reflection
La clase Type constituye la base de la librería de reflexión, encapsulando la información de las clases de un ensamblado así como acciones tales como la creación de objetos en runtime, etc.
Todos los objetos de .NET derivan en última instancia de la clase Object. Dicha clase define un método virtual llamado GetType que nos permite obtener la instancia de tipo Type que representa dicha clase. Cada Type define única y exclusivamente una clase del código y se garantiza que si y solo si dos objetos pertenencen a la misma clase entonces su Type es el mismo.
Para nuestro caso sin embargo vamos a centrarnos en una función llamada GetCustomAttributes de la clase Type, o su correpondiente estático de la clase Attribute. Dicha función nos permite elaborar los atributos asociados a cualquier objeto definido en el código. Veamos su definición:
La función nos permite recuperar los atributos asignados a un elemento de código, en este caso un objeto, especificando además si deseamos recorrer toda la cadena de herencia hacía los padres (inherit) y si queremos que nos devuelva tan solo aquellos atributos de un determinado tipo.
Por ejemplo, supongamos que tenemos una función que recibe cualquier tipo de objeto, comprueba si implementa un interface y en caso de que lo implemente realiza alguna acción. Sin embargo, para aumentar la seguridad, deseamos comprobar antes de realizar ninguna acción si el objeto recibido está obsoleto:
Otro ejemplo relacionado con aquel del motor gráfico que vimos en el artículo anterior. Supongamos que tenemos una función que procesa una colisión contra un objeto. Para ello se le pasa a dicha función el objeto que recibe la colisión y un objeto de tipo Collision que recoge la información sobre la fuerza y dirección del impacto. Ahora bien, solo queremos tener en cuenta dicha colisión si el objeto que la recibe no es inamovible, o, por ejemplo, podríamos comprobar otras condiciones del objeto:
Además de la clase Type podemos acceder a los atributos de cualquier elemento en el que, de hecho, se pueda definir un atributo. Eso quiere decir que podemos acceder a los atributos de un ensamblado mediante la función Assembly.GetCustomAttributes, a los de un metodo mediante MethodInfo.GetCustomAttributes, etc. Otro método para acceder a ellos es utilizar la función estática Attribute.GetCustomAttributes que admite varias sobrecargas en función del tipo que queramos pasar, es decir, en función de si pasamos un Type, un MemberInfo, un AssemblyInfo, etc
Hemos visto que podemos acceder a la información de los atributos de, casi, cualquier elemento de código. Por otro lado, la posibilidad de .NET de cargar cualquier ensamblado en tiempo de ejecución mediante el uso de Reflection nos permite no solamente acceder a los atributos de los objetos físicos que nos pasan sino examinar cualquier ensamblado, objeto por objeto y método por método en busca de un determinado atributo lo cual nos abre un enorme abanico de posibilidades.
Para ver un ejémplo práctico del uso de atributos en una aplicación real, y aunque es un ejemplo razonablemente complejo, os recomiendo que le echéis un vistazo al artículo Sistema de Plugins con C# y .NET en el que se hace un uso bastante extenso de diversos atributos para contemplar toda la información de los diversos plugins.