martes, 19 de enero de 2016

Atributos en C#

Existen usos muy importantes de los atributos, los cuales se aplican para seguridad, como por ejemplo validar la autorización del uso de la clase, la definición de servicios, es decir de los contratos para los servicios [ServiceContract], de los métodos de los contratos [OperationContract]; en la definición de clases para definir el modelo de datos en MVC, métodos, etc.

Estos atributos crean la metadata que se puede utilizar en el proceso de compilación, además de otros importantes usos, y de ahí su importancia el manejar estos elementos del lenguaje.

Los atributos son etiquetas declarativas, es decir información adicional. Esta información se puede recuperar en tiempo de ejecución por medio del mecanismo de reflexión. Pueden usarse atributos predefinidos o personalizarlos.

Los atributos pueden ser colocados en la mayoría de las declaraciones (aunque un atributo especifico podría estar restringido a los tipos de declaraciones). La sintaxis, define que se coloque entre paréntesis cuadrados adelante de la declaración de la entidad a la que se aplica, por ejemplo el atributo Conditional.

[Conditional("DEBUG")] public class myClaseDebug


Aplicación de los atributos
En ocasiones la aplicación de atributos puede resulta ambiguo, veamos el siguiente ejemplo

[MiAtributo] int miMetodo (string cadenaTexto)

En el ejemplo es necesario saber con certeza a quien se aplica el atributo, ya sea al método o al valor de retorno. Para resolver estas situaciones, existen elementos conocidos como "destinos" predeterminados.

Por omisión el atributo aplica al método:

[MiAtributo] int miMetodo

Puede reemplazarse por especificaciones explicitas, por ejemplo:

[method: MiAtributo ] int miMetodo
[return: OtroAtributo] int miMetodo

La sintaxis general es:

[destino: lista-de-atributos]

El destino (target), es uno de los posibles destino assembly, field, module, param, property, return o type. La lista de atributos, se refiere a los atributos a aplicar.

Declaración
Destinos posibles
assembly assembly [assembly: lista-atributos]
module module    [module: lista-atributos]
class type
struct type
interface type
enum type
delegate type, return
method method, return
parameter param
field field
property — indexer property
property — get accessor method, return
property — set accessor method, param, return
event — field event, field, method
event — property event, property
event — add method, param
event — remove method, param

Atributos Globales
La mayoria de los atributos se relacionan a elementos específicos de lenguaje, como clases o métodos; sin embargo, algunos atributos son globales - estos se aplican a un ensamblado completo o un módulo

[assembly: lista-tributos]

[module: lista-atributos]

Los atributos globales aparecen después de las declaraciones using y antes que las declaraciones de espacio de nombres o de tipo, pueden aparecer en cualquier cantidad de archivos fuente pero sólo para una compilación. Por ejemplo:

[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyTitle("MiAplicacion")]

Otros atributos de uso más frecuente:

AssemblyCompanyAttribute
AssemblyConfigurationAttribute
AssemblyCopyrightAttribute
AssemblyCultureAttribute
AssemblyDescriptionAttribute
AssemblyProductAttribute
AssemblyTrademarkAttribute
NOTA
En el uso cotidiano es posible retirar del nombre del atributo el sufijo Attribute sin mayor problema

Crear atributos personalizados
Es posible crear atributos personalizados creando una clase de tipo atributo, ésta se deriva directamente de System.Attribute, esto permite identificar definiciones de atributos como metadatos de forma rápida y sencilla. Enseguida un ejemplo.

using System;

namespace AtributosPersonalizados
{

 
    /// <summary>
    /// La clase crea un atributo personalizado y documenta al desarrollador que
    /// creo la clase.
    ///
    /// El uso de System.Attribute, como clase base
    /// define que se trata de un atributo personalizado.
    ///
    /// El uso de AttributeUsage, se define para validar que el atributo
    /// Desarrollador solo se usara para clases y estructuras.
    /// El parametro con nombre: AllowMultiple, permite que el atributo sea
    /// de un solo uso o multiple.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct, AllowMultiple = true)]
    public class Desarrollador: Attribute
    {
        string _nombre;
        public string version;

        /// <summary>
        /// El constructor toma el nombre y define un valor
        /// por omision para la versión.
        /// </summary>
        /// <param name="nombre"></param>
        public Desarrollador(string nombre)
        {
            this._nombre = nombre;
            this.version = "1.0.0.0";
        }
    }

    // Ejemplo de la declaración de una clase con el uso del
    // atributo personalizado
    [Desarrollador("Pedro Perez", version ="2.0.0.0")]
    public class ClaseConAtributoPersonalizado
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
        }
    }
}

Reflexion, recuperación de información de atributos
Esta tarea se realiza por medio del método GetCustomAttributes, el cual nos proporciona una matriz de objetos equivalentes a los atributos de código fuente, en tiempo de ejecución.

Enseguida un ejemplo del uso de del método GetCustomAttributes.
using System;

namespace AtributosPersonalizados
{

 
    /// <summary>
    /// La clase crea un atributo personalizado y documenta al desarrollador que
    /// creo la clase.
    ///
    /// El uso de System.Attribute, como clase base
    /// define que se trata de un atributo personalizado.
    ///
    /// El uso de AttributeUsage, se define para validar que el atributo
    /// Desarrollador solo se usara para clases y estructuras.
    /// El parametro con nombre: AllowMultiple, permite que el atributo sea
    /// de un solo uso o multiple.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class|AttributeTargets.Struct, AllowMultiple = true)]
    public class Desarrollador: Attribute
    {
        string _nombre;
        public string version;

        /// <summary>
        /// El constructor toma el nombre y define un valor
        /// por omision para la versión.
        /// </summary>
        /// <param name="nombre"></param>
        public Desarrollador(string nombre)
        {
            this._nombre = nombre;
            this.version = "1.0.0.0";
        }


        // Obtiene el nombre del desarrollador
        public string NombreDesarrollador()
        {
            return _nombre;
        }


    }

    // Ejemplo de la declaración de una clase con el uso del
    // atributo personalizado
    [Desarrollador("Pedro Perez", version ="2.0.0.0")]
    public class ClaseConAtributoPersonalizado
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Información de la clase ClaseConAtributoPersonalizado");
            Attribute[] atributosClase = Attribute.GetCustomAttributes(typeof(ClaseConAtributoPersonalizado));
            foreach (Attribute atributo in atributosClase)
            {
                if(atributo is Desarrollador)
                {
                    Desarrollador d = (Desarrollador)atributo;
                    Console.WriteLine(" Nombre: {0}, version: {1} ", d.NombreDesarrollador(), d.version);
                }
            }
            Console.ReadKey();

        }

     

    }
}



Referencias:
https://msdn.microsoft.com/es-es/library/aa288182(v=vs.71).aspx
https://msdn.microsoft.com/es-es/library/aa287893(v=vs.71).aspx
https://msdn.microsoft.com/es-es/library/aa287991(v=vs.71).aspx






No hay comentarios.:

Publicar un comentario