viernes, 6 de junio de 2014

Receta No. 3-13 en C#: Crear un Atributo Personalizado

Tabla de Contenido

0. Introducción
1. Problema
2. Solución
3. Discusión de la Solución
4. Práctica: Código Fuente C#
4.1 Atributo personalizado Autor
4.2 Ejemplo con atributos: VerificadoAttribute y AutorAttribute
Conclusiones
Glosario
Literatura & Enlaces

0. Introducción

Esta receta va a hacer muy interesante, pues vamos a explorar otro concepto importante en la definición de tipos con marcas especiales (o decoraciones) para tipos y miembros, conocidos como Atributos (Attributes). Estos atributos permiten especificar meta-información a los tipos y miembros que definimos para nuestras aplicaciones. Veremos que esta meta-información puede ser consultada de forma dinámica a través de reflection en tiempo de ejecución. ¡Comencemos!

1. Problema

Queremos adjuntar información a los assemblies, tipos, miembros y otras construcciones para, por ejemplo, especificar la autoría, validez de un miembro, versionamiento, &c, y que esta información pueda ser consultada usando reflection en tiempo de ejecución.

2. Solución

Nuevamente el equipo de formación de Ad Infinitum Systems nos ha apoyado con el conocimiento y práctica para resolver nuestro requerimiento técnico. La solución: uso de atributos personalizados.

Para la creación de atributos personalizados, debemos tomar en cuenta:
  • Creación de un subtipo que derive directa o indirectamente de System.Attribute.
  • Implementación de constructores, campos, y propiedades que permitan al usuario del atributo especificar su configuración en el instante de la decoración o aplicación.
  • Crear restricciones sobre los tipos y miembros en donde puede ser usado un determinado atributo personalizado a través de la clase System.AttributeUsageAttribute. En general, permite [1]:
    • Elementos de programa (tipos, miembros) que pueden usar el atributo.
    • Uso múltiple del atributo para un elemento del programa.
    • Permitir o bloquear la herencia desde el atributo personalizado.

3. Discusión de la solución

3.1 Definición concepto de atributo en C#

Un atributo en lenguaje de programación C# es un mecanismo que permite asociar información (meta-datos) a los tipos o miembros de los elementos de programa creados por el programador. Los meta-datos asociados con un elemento de programa se describen en los contenedores de descripción del assembly en donde los primeros están definidos. La condición previa va a permitir, a través de reflection (Crear un Objecto con Reflection) consultar la información asociada al elemento de programa por medio de los atributos aplicados sin necesidad de crear un objeto del tipo correspondiente.

En [1] nos informan:
...the CLR, use this information to determine how to interact with and manage program elements.

3.2 Creación de atributos personalizados

Para crear un atributo personalizado debemos definir una clase concreta que herede de la clase Attribute [6] (N:System):

[aplicacion-otros-atributos]
modificador-acceso class identificador : System.Attribute
{
// Implementación...
// DEBE POSEER AL MENOS UN CONSTRUCTOR PÚBLICO
public identificador()
{ }
}

Para poner como caso, creemos un atributo que nos sirva para marcar los elementos de programa (tipos, y miembros) definidos por el programador con su autoría y la versión del elemento:

Archivo de código fuente AutorAttribute.cs [enlace alternativo]:
En las líneas 5 y 6 hemos especificado a través del atributo AttributeUsageAttribute [7] (N:System) los elementos de programa en los que puede ser aplicado el atributo: AttributeTargets.Class, y AttributeTargets.Struct. Es decir, para clases y estructuras. Vale añadir que AttributeTargets es una enumeración, y que gracias a la operación bitwise OR (|) podemos hacer la combinación con los valores de la enumeración de la siguiente manera:

System.AttributeTargets.Class | System.AttributeTargets.Struct

En la línea 8 especificamos que AutorAttribute hereda directamente de la clase System.AttributeAutorAttribute : Attribute.


En el cuerpo de declaración de la clase AutorAttribute especificamos los siguientes miembros:
  • nombre: argumento opcional (puede ser especificado en aplicación del atributo en tipo o miembro).
  • version: argumento requerido o argumento posicional (debe ser especificado sobre un tipo o miembro).
  • AutorAttribute: Constructor que inicializa el nombre del autor del elemento de programa.
Con respecto a lo inmediatamente anterior, es importante aclarar que un argumento requerido es cualquier miembro del atributo personalizado que se marca como public o cuenta con funciones de acceso de lectura (get) y escritura (set) sobre una propiedad. El caso contrario, aplica para los argumentos opcionales,  que son aquellos que cuentan con una definición de sólo lectura.

Continuando, en las líneas 21-24 se define la clase ClaseEjemplo. A esta clase se le aplica el atributo personalizado AutorAttribute con:

[Autor("John Ortiz Ordoñez", version = 1.1)]

Observemos dos cosas:
  1. Hemos omitido el nombre completo del atributo. Esto está permitido, dado que el compilador asume que el postfijo es una parte opcional del atributo personalizado. Pudimos haberlo especificado completamente

    [AutorAttribute("John Ortiz", version = 1.1)]

    y también es válido.
  2. El primer valor "John Ortiz" es parte del constructor del atributo personalizado, mientras que version = 1.1 es requerido (argumento requerido).
Otros apuntes importantes:
  • La clase de atributo personalizado debe poseer al menos un constructor público (aunque basta con el definido por defecto automáticamente).
  • El atributo personalizado puede poseer uno o más constructores. Esto va a permitir al programador usar el atributo de distintos maneras (grado de granularidad).
  • En la Figura 1 [1] se muestra la lista de argumentos (opcionales y requeridos) de la clase (atributo personalizado) AttributeUsageAttribute.
Parámetros del atributo AttributeUsageAttribute
Figura 1. Parámetros del atributo AttributeUsageAttribute [1, 7].

4. Práctica: Código Fuente C#

4.1 Atributo personalizado Autor

En este ejemplo vamos a extender el atributo personalizado que creamos en la sección 3.2.

Archivo de código fuente AutorAttribute.cs [enlace alternativo]:
A destacar las líneas 5 y 6, hemos usado los argumentos opcionales AllowMultiple = true para permitir la aplicación del atributo múltiples veces sobre el mismo elemento de programa, y con Inherited = false restringimos la herencia de este atributo personalizado. El resto de la implementación del atributo personalizado no introduce nuevos conceptos respecto a los visto en la sección 3.

Uso:


Ahora usemos el atributo personalizado definido en el archivo AutorAttribute.cs para marcar o decorar un assembly y un par de clases.

Archivo de código fuente DecoracionConAutorAttribute.cs [enlace alternativo]:

Podemos marcar (o decorar) un assembly con un atributo tal y como ocurre en la línea 4:

[assembly: Recetas.Cap03.AutorAttribute("Valentina", Organizacion = "OrtizOL")]


Hay que recordar que la configuración de un assembly siempre debe realizarse antes que cualquier declaración de elemento programa.

Por otro lado, decoramos las clases Orden:

[AutorAttribute("Valentina", Organizacion = "OrtizOL")]

Producto:

[AutorAttribute("Valentina", Organizacion = "OrtizOL")]
[AutorAttribute("Juan", Organizacion = "OrtizOL")]


Para este último caso utilizamos dos veces el atributo AutorAttribute, esto gracias a la especificación de AllowMultiple = true en el atributo.

4.2 Ejemplo con atributos: VerificadoAttribute y AutorAttribute

Archivo de código fuente AtributosYReflection.cs [enlace alternativo]:
Compilación:

  1. csc /target.exe AtributosYReflection.cs

Ejecución:


  1. .\AtributosYReflection.exe

Resultado:
Resultado ejecución atributos personalizados y reflection
Figura 2. Resultado ejecución atributos personalizados y reflection.

Conclusiones

Por medio del desarrollo de esta receta hemos aprendido a declarar y usar atributos personalizados; los cuales son útiles para decorar con meta-datos los elementos de programa (tipos y miembros) que definamos en nuestra solución. Los meta-datos pueden ser cargados, investigados, y manipulados en tiempo de ejecución por medio de reflection sin necesidad de crear instancias de los tipos de los assemblies cargados en el dominio de aplicación.

Glosario

  • Argumento opcional
  • Argumento requerido
  • Assembly
  • Atributo personalizado
  • Elemento de programa
  • Argumento opcional
  • Reflection

Literatura & Enlaces

[1]: Visual C# 2010 Recipes by Allen Jones and Adam Freeman. Copyright 2010 Allen Jones and Adam Freeman, 978-1-4302-2525-6.
[2]: Attribute Class (System) - http://msdn.microsoft.com/en-us/library/system.attribute(v=vs.110).aspx
[3]: Creating Custom Attributes (C# and Visual Basic) - http://msdn.microsoft.com/en-us/library/sw480ze8.aspx
[4]: Attributes Tutorial (C#) - http://msdn.microsoft.com/en-us/library/aa288454(v=vs.71).aspx
[5]: Receta No. 3-12 en C#: Crear un Objeto con Reflection | OrtizOL - Experiencias Construcción Software (xCSw) - http://ortizol.blogspot.com/2014/06/receta-no-3-12-en-c-crear-un-objeto-con-reflection.html
[6]: Attribute Class (System) - http://msdn.microsoft.com/en-us/library/system.attribute(v=vs.110).aspx
[7]: AttributeUsageAttribute Class (System) - http://msdn.microsoft.com/en-us/library/system.attributeusageattribute(v=vs.110).aspx
[8]: AttributeTargets Enumeration (System) - http://msdn.microsoft.com/en-us/library/system.attributetargets(v=vs.110).aspx


J

No hay comentarios:

Publicar un comentario

Envíe sus comentarios, dudas, sugerencias, críticas. Gracias.