Escribir atributos personalizados

Actualización: noviembre 2007

Para diseñar atributos propios personalizados no se requiere conocer a fondo muchos conceptos nuevos. Si está familiarizado con programación orientada a objetos y sabe diseñar clases, posee la práctica totalidad de conocimientos necesarios. Los atributos personalizados son esencialmente clases tradicionales que se derivan directa o indirectamente de System.Attribute. Al igual que las clases tradicionales, los atributos personalizados contienen métodos que almacenan y recuperan datos.

A continuación se enumeran los pasos principales para diseñar correctamente clases de atributos personalizados.

  • Aplicar el atributo AttributeUsageAttribute

  • Declarar la clase de atributo

  • Declarar constructores

  • Declarar propiedades

En esta sección se describen cada uno de estos pasos y dicha sección termina con un ejemplo de atributo personalizado.

Aplicar el atributo AttributeUsageAttribute

La declaración de un atributo personalizado empieza con el atributo AttributeUsageAttribute, que define algunas de las características clave de la clase de atributo. Por ejemplo, se puede especificar si el atributo puede ser heredado por otras clases o a qué elementos se puede aplicar el atributo. El fragmento de código siguiente muestra cómo se utiliza el atributo AttributeUsageAttribute.

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
<AttributeUsage(AttributeTargets.All, Inherited := False, AllowMultiple := true)>

System.AttributeUsageAttribute tiene tres miembros que son importantes para la creación de atributos personalizados: AttributeTargets, Inherited y AllowMultiple.

AttributeTargets (Miembro)

En el ejemplo anterior, se especifica AttributeTargets.All, indicando que este atributo se puede aplicar a todos los elementos de programa. También se puede especificar AttributeTargets.Class, que indica que el atributo puede aplicarse sólo a una clase, o bien AttributeTargets.Method que indica que el atributo puede aplicarse sólo a un método. Todos los elementos de programa pueden ser marcados a efectos de descripción por un atributo personalizado de este modo.

También se pueden pasar varias instancias de AttributeTargets. El fragmento de código siguiente especifica que un atributo personalizado se puede aplicar a cualquier clase o método.

[AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]
<AttributeUsage (AttributeTargets.Class Or AttributeTargets.Method)>

Inherited (Propiedad)

La propiedad Inherited indica si el atributo puede ser heredado por clases derivadas de las clases a las que se aplica el atributo. Esta propiedad puede tomar el valor true (valor predeterminado) o false. Por ejemplo, en el siguiente ejemplo de código, MyAttribute tiene el valor predeterminado true para Inherited, mientras que YourAttribute tiene el valor false para esta misma propiedad.

//This defaults to Inherited = true.
public class MyAttribute :Attribute
{
}
[AttributeUsage( Inherited = false)]
public class YourAttribute : Attribute
{
}
<AttributeUsage( AttributeTargets.All, Inherited := True)> Public Class _
MyAttribute
    Inherits Attribute
End Class

<AttributeUsage( AttributeTargets.All, Inherited := False)> Public Class _
YourAttribute
    Inherits Attribute
End Class

Los dos atributos se aplican después a un método de la clase base MyClass.

public class MyClass
{
    [MyAttribute]
    [YourAttribute]
    public virtual void MyMethod() 
    {
        //...
    }
}
' In Microsoft Visual Basic, you apply multiple attributes
' by separating them with commas.
Public Class [MyClass]
    <MyAttribute, YourAttribute> Public Overridable Sub MyMethod()
        '...
    End Sub
End Class

Finalmente, la clase YourClass se hereda de la clase base MyClass. El método MyMethod muestra MyAttribute, pero no YourAttribute.

public class YourClass: MyClass
{
      //MyMethod will have MyAttribute but not YourAttribute.
      public override void MyMethod()
      {
         //...
      }

}
Public Class YourClass
   Inherits [MyClass]
      'MyMethod will have MyAttribute but not YourAttribute.
      Public overrides Sub MyMethod()
         '...
      End Sub
End Class

AllowMultiple (Propiedad)

La propiedad AllowMultiple indica si pueden existir varias instancias del atributo en un elemento. Si se establece en true, se permiten varias instancias; si se establece en false (valor predeterminado), sólo se permite una instancia.

En el siguiente ejemplo de código, MyAttribute tiene el valor predeterminado false para AllowMultiple, mientras que YourAttribute tiene el valor true.

//This defaults to AllowMultiple = false.
public class MyAttribute :Attribute
{
}

[AttributeUsage(AllowMultiple = true)]
public class YourAttribute : Attribute
{
}
'This defaults to AllowMultiple = false.
<AttributeUsage(AttributeTargets.Method)> Public Class _
MyAttribute
    Inherits Attribute
End Class

<AttributeUsage(AttributeTargets.Method, AllowMultiple := True)> Public Class _
YourAttribute
    Inherits Attribute
End Class

Cuando se aplican varias instancias de estos atributos, MyAttribute genera un error del compilador. El ejemplo de código siguiente muestra un uso válido de YourAttribute y un uso no válido de MyAttribute.

public class MyClass
{
    //This produces an error.
    //Duplicates are not allowed.
    [MyAttribute]
    [MyAttribute]
    public void MyMethod() {
        //...
    }

    //This is valid.
    [YourAttribute]
    [YourAttribute]
    public void YourMethod(){
    //...
    }
}
' In Microsoft Visual Basic you apply multiple attributes
' by separating them with commas.
Public Class [MyClass]
    'This produces an error.
    'Duplicates are not allowed.
    <MyAttribute, MyAttribute> Public Overridable Sub MyMethod()
        '...
    End Sub

    'This is valid.    
    <YourAttribute, YourAttribute> Public Sub YourMethod()
        '...
    End Sub
End Class

Si las propiedades AllowMultiple e Inherited se establecen ambas en true, una clase heredada de otra puede heredar un atributo y tener aplicada otra instancia del mismo atributo en la misma clase secundaria. Si la propiedad AllowMultiple se establece en false, los valores de los atributos de la clase primaria serán sobrescritos por las instancias nuevas del mismo atributo de la clase secundaria.

Declarar la clase de atributo

Después de aplicar el atributo AttributeUsageAttribute, se puede empezar a definir las especificaciones del atributo. La declaración de una clase de atributo es similar a la declaración de una clase tradicional, como muestra el código siguiente.

public class MyAttribute : System.Attribute 
{
    // . . . 
}
' This attribute is only usable with methods
<AttributeUsage(AttributeTargets.Method)> Public Class MyAttribute
    Inherits System.Attribute
    ' . . . 
End Class

Esta definición de atributo muestra los puntos siguientes:

  • Las clases de atributo se deben declarar como clases de tipo public.

  • Por convención, el nombre de la clase de atributo termina con la palabra Attribute. Aunque no se requiera, se recomienda esta convención por cuestiones de legibilidad. Cuando se aplica el atributo, la inclusión de la palabra Attribute es opcional.

  • Todas las clases de atributo deben heredar directa o indirectamente de System.Attribute.

  • En Microsoft Visual Basic, todas las clases de atributos personalizados deben tener el atributo AttributeUsageAttribute.

Declarar constructores

Los atributos se inicializan con constructores del mismo modo que las clases tradicionales. El fragmento de código siguiente muestra un constructor de atributos típico. Este constructor de tipo public acepta un parámetro y establece su valor en una variable miembro.

public MyAttribute(bool myvalue)
{
    this.myvalue = myvalue;        
}
Public Sub New(newvalue As Boolean)
    Me.myvalue = newvalue
End Sub

Se puede sobrecargar el constructor para que incluya diferentes combinaciones de valores. Si también se define una propiedad para la clase de atributo personalizada, se puede utilizar una combinación de parámetros con nombre y parámetros posicionales al inicializar el atributo. Normalmente, se definen todos los parámetros requeridos como posicionales y todos los parámetros opcionales como parámetros con nombre. En este caso, el atributo no se puede inicializar sin el parámetro requerido. Todos los demás parámetros son opcionales. Tenga en cuenta que, en Visual Basic, los constructores de una clase de atributos no deberían utilizar un argumento ParamArray.

El ejemplo de código siguiente muestra cómo un atributo que utiliza el constructor anterior se puede aplicar utilizando parámetros requeridos y opcionales. Se supone que el atributo tiene un valor booleano requerido y una propiedad de cadena opcional.

//One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
//One required (positional) parameter is applied.
[MyAttribute(false)]
'One required (positional) and one optional (named) parameter are applied.
<MyAttribute(False, OptionalParameter := "optional data")>
' ...
'One required (positional) parameter is applied.
<MyAttribute(False)>

Declarar propiedades

Si se desea definir un parámetro con nombre o proporcionar un método sencillo para devolver los valores almacenados por el atributo, se ha de declarar una propiedad. Las propiedades de atributo se deben declarar como entidades de tipo public con una descripción del tipo de datos que se devolverá. Se ha de definir la variable que contendrá el valor de la propiedad y asociarla a los métodos get y set. El ejemplo de código siguiente muestra cómo se implementa una propiedad simple en el atributo.

public bool MyProperty
{
    get {return this.myvalue;}
    set {this.myvalue = value;}
}
Public Property MyProperty As Boolean
    Get
        Return Me.myvalue
    End Get
    Set
        Me.myvalue = value
    End Set
End Property

Ejemplo de atributo personalizado

Esta sección incorpora la información anterior y muestra cómo se diseña un atributo simple que documenta información relativa al autor de una sección de código. El atributo de este ejemplo almacena el nombre y el nivel del programador, y si se ha revisado el código. Utiliza tres variables de tipo private para almacenar los valores reales que se han de guardar. Cada variable se representa mediante una propiedad de tipo public que obtiene y establece los valores. Finalmente, el constructor se define con dos parámetros requeridos.

[AttributeUsage(AttributeTargets.All)]
public class DeveloperAttribute : System.Attribute 
{
        
    //Private fields.
    private string name;
    private string level;
    private bool reviewed;

    //This constructor defines two required parameters: name and level.

    public  DeveloperAttribute(string name,string level)
    {
        this.name = name;
        this.level = level; 
        this.reviewed = false;
    }

    //Define Name property.
    //This is a read-only attribute.
        
    public virtual string Name
    {
        get {return name;}        
    }

    //Define Level property.
    //This is a read-only attribute.
        
    public virtual string Level
    {
        get {return level;}
    }

    //Define Reviewed property. 
    //This is a read/write attribute. 

    public virtual bool Reviewed
    {
        get {return reviewed;}
    set {reviewed = value;}
    }
}
<AttributeUsage(AttributeTargets.All)> Public Class DeveloperAttribute
    Inherits System.Attribute
    
    'Private fields.
    Private m_name As String
    Private m_level As String
    Private m_reviewed As Boolean    
    
    'This constructor defines two required parameters: name and level.
    Public Sub New(name As String, level As String)
        Me.m_name = name
        Me.m_level = level
        Me.m_reviewed = False
    End Sub
    
    'Define Name property.
    'This is a read-only attribute.    
    Public Overridable ReadOnly Property Name() As String
        Get
            Return m_name
        End Get
    End Property 
    
    'Define Level property.
    'This is a read-only attribute.    
    Public Overridable ReadOnly Property Level() As String
        Get
            Return m_level
        End Get
    End Property
    
    'Define Reviewed property. 
    'This is a read/write attribute.    
    Public Overridable Property Reviewed() As Boolean
        Get
            Return m_reviewed
        End Get
        Set
            m_reviewed = value
        End Set
    End Property
End Class

Se puede aplicar este atributo utilizando el nombre completo, DeveloperAttribute, o el nombre abreviado, Developer, mediante una de las formas siguientes.

[Developer("Joan Smith", "1")]
[Developer("Joan Smith", "1", Reviewed = true)]
<Developer("Joan Smith", "1")>
<Developer("Joan Smith", "1", Reviewed := True)>

El primer ejemplo muestra el atributo aplicado con sólo los parámetros con nombre requeridos, mientras que el segundo muestra el atributo aplicado con los parámetros requeridos y opcionales.

Vea también

Referencia

System.Attribute

AttributeUsageAttribute

Otros recursos

Extender metadatos mediante atributos