Escrever atributos personalizados
Para criar atributos personalizados, você não precisa aprender muitos conceitos novos. Se você está familiarizado com programação orientada a objetos e sabe como projetar classes, você já tem a maior parte do conhecimento necessário. Atributos personalizados são classes tradicionais que derivam direta ou indiretamente da System.Attribute classe. Assim como as classes tradicionais, os atributos personalizados contêm métodos que armazenam e recuperam dados.
As etapas principais para projetar corretamente classes de atributos personalizados são as seguintes:
Esta seção descreve cada uma dessas etapas e conclui com um exemplo de atributo personalizado.
Aplicando o AttributeUsageAttribute
Uma declaração de atributo personalizada começa com o System.AttributeUsageAttribute atributo, que define algumas das principais características da sua classe de atributo. Por exemplo, você pode especificar se seu atributo pode ser herdado por outras classes ou a quais elementos o atributo pode ser aplicado. O fragmento de código a seguir demonstra como usar o AttributeUsageAttribute:
[AttributeUsage(AttributeTargets::All, Inherited = false, AllowMultiple = true)]
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
<AttributeUsage(AttributeTargets.All, Inherited:=False, AllowMultiple:=True)>
Public Class SomeClass
Inherits Attribute
'...
End Class
O AttributeUsageAttribute tem três membros que são importantes para a criação de atributos personalizados: AttributeTargets, Herdado e AllowMultiple.
Membro AttributeTargets
No exemplo anterior, AttributeTargets.All é especificado, indicando que esse atributo pode ser aplicado a todos os elementos do programa. Como alternativa, você pode especificar AttributeTargets.Class, indicando que seu atributo pode ser aplicado somente a uma classe, ou AttributeTargets.Method, indicando que seu atributo pode ser aplicado somente a um método. Todos os elementos do programa podem ser marcados para descrição por um atributo personalizado dessa maneira.
Você também pode passar vários AttributeTargets valores. O fragmento de código a seguir especifica que um atributo personalizado pode ser aplicado a qualquer classe ou método:
[AttributeUsage(AttributeTargets::Class | AttributeTargets::Method)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method)>
Public Class SomeOtherClass
Inherits Attribute
'...
End Class
Propriedade Herdada
A AttributeUsageAttribute.Inherited propriedade indica se o atributo pode ser herdado por classes derivadas das classes às quais o atributo é aplicado. Esta propriedade usa um true
(o padrão) ou false
sinalizador. No exemplo a seguir, MyAttribute
tem um valor padrão Inherited de true
, enquanto YourAttribute
tem um Inherited valor de false
:
// This defaults to Inherited = true.
public ref class MyAttribute : Attribute
{
//...
};
[AttributeUsage(AttributeTargets::Method, Inherited = false)]
public ref class YourAttribute : Attribute
{
//...
};
// This defaults to Inherited = true.
public class MyAttribute : Attribute
{
//...
}
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class YourAttribute : Attribute
{
//...
}
' This defaults to Inherited = true.
Public Class MyAttribute
Inherits Attribute
'...
End Class
<AttributeUsage(AttributeTargets.Method, Inherited:=False)>
Public Class YourAttribute
Inherits Attribute
'...
End Class
Os dois atributos são então aplicados a um método na classe MyClass
base:
public ref class MyClass
{
public:
[MyAttribute]
[YourAttribute]
virtual void MyMethod()
{
//...
}
};
public class MyClass
{
[MyAttribute]
[YourAttribute]
public virtual void MyMethod()
{
//...
}
}
Public Class MeClass
<MyAttribute>
<YourAttribute>
Public Overridable Sub MyMethod()
'...
End Sub
End Class
Finalmente, a classe YourClass
é herdada da classe MyClass
base. O método MyMethod
mostra, MyAttribute
mas não YourAttribute
:
public ref class YourClass : MyClass
{
public:
// MyMethod will have MyAttribute but not YourAttribute.
virtual void MyMethod() override
{
//...
}
};
public class YourClass : MyClass
{
// MyMethod will have MyAttribute but not YourAttribute.
public override void MyMethod()
{
//...
}
}
Public Class YourClass
Inherits MeClass
' MyMethod will have MyAttribute but not YourAttribute.
Public Overrides Sub MyMethod()
'...
End Sub
End Class
Propriedade AllowMultiple
A AttributeUsageAttribute.AllowMultiple propriedade indica se várias instâncias do seu atributo podem existir em um elemento. Se definido como true
, várias instâncias são permitidas. Se definido como false
(o padrão), apenas uma instância é permitida.
No exemplo a seguir, MyAttribute
tem um valor padrão AllowMultiple de false
, enquanto YourAttribute
tem um valor de true
:
//This defaults to AllowMultiple = false.
public ref class MyAttribute : Attribute
{
};
[AttributeUsage(AttributeTargets::Method, AllowMultiple = true)]
public ref class YourAttribute : Attribute
{
};
//This defaults to AllowMultiple = false.
public class MyAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class YourAttribute : Attribute
{
}
' This defaults to AllowMultiple = false.
Public Class MyAttribute
Inherits Attribute
End Class
<AttributeUsage(AttributeTargets.Method, AllowMultiple:=true)>
Public Class YourAttribute
Inherits Attribute
End Class
Quando várias instâncias desses atributos são aplicadas, MyAttribute
produz um erro de compilador. O exemplo de código a seguir mostra o uso válido e YourAttribute
o uso inválido de MyAttribute
:
public ref class MyClass
{
public:
// This produces an error.
// Duplicates are not allowed.
[MyAttribute]
[MyAttribute]
void MyMethod()
{
//...
}
// This is valid.
[YourAttribute]
[YourAttribute]
void YourMethod()
{
//...
}
};
public class MyClass
{
// This produces an error.
// Duplicates are not allowed.
[MyAttribute]
[MyAttribute]
public void MyMethod()
{
//...
}
// This is valid.
[YourAttribute]
[YourAttribute]
public void YourMethod()
{
//...
}
}
Public Class MyClass
' This produces an error.
' Duplicates are not allowed.
<MyAttribute>
<MyAttribute>
Public Sub MyMethod()
'...
End Sub
' This is valid.
<YourAttribute>
<YourAttribute>
Public Sub YourMethod()
'...
End Sub
End Class
Se a AllowMultiple propriedade e a Inherited propriedade estiverem definidas como true
, uma classe herdada de outra classe poderá herdar um atributo e ter outra instância do mesmo atributo aplicada na mesma classe filha. Se AllowMultiple estiver definido como false
, os valores de quaisquer atributos na classe pai serão substituídos por novas instâncias do mesmo atributo na classe filha.
Declarando a classe de atributo
Depois de aplicar o AttributeUsageAttribute, comece a definir as especificidades do seu atributo. A declaração de uma classe de atributo é semelhante à declaração de uma classe tradicional, como demonstrado pelo código a seguir:
[AttributeUsage(AttributeTargets::Method)]
public ref class MyAttribute : Attribute
{
// . . .
};
[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
// . . .
}
<AttributeUsage(AttributeTargets.Method)>
Public Class MyAttribute
Inherits Attribute
' . . .
End Class
Esta definição de atributo demonstra os seguintes pontos:
As classes de atributos devem ser declaradas como classes públicas.
Por convenção, o nome da classe de atributo termina com a palavra Atributo. Embora não seja necessária, esta convenção é recomendada para facilitar a leitura. Quando o atributo é aplicado, a inclusão da palavra Atributo é opcional.
Todas as classes de atributo devem herdar direta ou indiretamente da System.Attribute classe.
No Microsoft Visual Basic, todas as classes de atributo personalizado devem ter o System.AttributeUsageAttribute atributo.
Declarando construtores
Assim como as classes tradicionais, os atributos são inicializados com construtores. O fragmento de código a seguir ilustra um construtor de atributo típico. Este construtor público usa um parâmetro e define uma variável membro igual ao seu valor.
MyAttribute(bool myvalue)
{
this->myvalue = myvalue;
}
public MyAttribute(bool myvalue)
{
this.myvalue = myvalue;
}
Public Sub New(myvalue As Boolean)
Me.myvalue = myvalue
End Sub
Você pode sobrecarregar o construtor para acomodar diferentes combinações de valores. Se você também definir uma propriedade para sua classe de atributo personalizada, poderá usar uma combinação de parâmetros nomeados e posicionais ao inicializar o atributo. Normalmente, você define todos os parâmetros necessários como posicionais e todos os parâmetros opcionais como nomeados. Nesse caso, o atributo não pode ser inicializado sem o parâmetro necessário. Todos os outros parâmetros são opcionais.
Nota
No Visual Basic, os construtores de uma classe de atributo não devem usar um ParamArray
argumento.
O exemplo de código a seguir mostra como um atributo que usa o construtor anterior pode ser aplicado usando parâmetros opcionais e necessários. Ele assume que o atributo tem um valor booleano necessário e uma propriedade string opcional.
// One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
public ref class SomeClass
{
//...
};
// One required (positional) parameter is applied.
[MyAttribute(false)]
public ref class SomeOtherClass
{
//...
};
// One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
public class SomeClass
{
//...
}
// One required (positional) parameter is applied.
[MyAttribute(false)]
public class SomeOtherClass
{
//...
}
' One required (positional) and one optional (named) parameter are applied.
<MyAttribute(false, OptionalParameter:="optional data")>
Public Class SomeClass
'...
End Class
' One required (positional) parameter is applied.
<MyAttribute(false)>
Public Class SomeOtherClass
'...
End Class
Declarando propriedades
Se você quiser definir um parâmetro nomeado ou fornecer uma maneira fácil de retornar os valores armazenados pelo seu atributo, declare uma propriedade. As propriedades de atributo devem ser declaradas como entidades públicas com uma descrição do tipo de dados que será retornado. Defina a variável que manterá o valor da sua propriedade e associe-a aos get
métodos and set
. O exemplo de código a seguir demonstra como implementar uma propriedade em seu atributo:
property bool MyProperty
{
bool get() {return this->myvalue;}
void set(bool value) {this->myvalue = value;}
}
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
Exemplo de atributo personalizado
Esta seção incorpora as informações anteriores e mostra como criar um atributo que documenta informações sobre o autor de uma seção de código. O atributo neste exemplo armazena o nome e o nível do programador e se o código foi revisado. Ele usa três variáveis privadas para armazenar os valores reais a serem salvos. Cada variável é representada por uma propriedade pública que obtém e define os valores. Finalmente, o construtor é definido com dois parâmetros necessários:
[AttributeUsage(AttributeTargets::All)]
public ref class DeveloperAttribute : Attribute
{
// Private fields.
private:
String^ name;
String^ level;
bool reviewed;
public:
// This constructor defines two required parameters: name and level.
DeveloperAttribute(String^ name, String^ level)
{
this->name = name;
this->level = level;
this->reviewed = false;
}
// Define Name property.
// This is a read-only attribute.
virtual property String^ Name
{
String^ get() {return name;}
}
// Define Level property.
// This is a read-only attribute.
virtual property String^ Level
{
String^ get() {return level;}
}
// Define Reviewed property.
// This is a read/write attribute.
virtual property bool Reviewed
{
bool get() {return reviewed;}
void set(bool value) {reviewed = value;}
}
};
[AttributeUsage(AttributeTargets.All)]
public class DeveloperAttribute : 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 Attribute
' Private fields.
Private myname As String
Private mylevel As String
Private myreviewed As Boolean
' This constructor defines two required parameters: name and level.
Public Sub New(name As String, level As String)
Me.myname = name
Me.mylevel = level
Me.myreviewed = False
End Sub
' Define Name property.
' This is a read-only attribute.
Public Overridable ReadOnly Property Name() As String
Get
Return myname
End Get
End Property
' Define Level property.
' This is a read-only attribute.
Public Overridable ReadOnly Property Level() As String
Get
Return mylevel
End Get
End Property
' Define Reviewed property.
' This is a read/write attribute.
Public Overridable Property Reviewed() As Boolean
Get
Return myreviewed
End Get
Set
myreviewed = value
End Set
End Property
End Class
Você pode aplicar esse atributo usando o nome completo, DeveloperAttribute
, ou usando o nome abreviado, Developer
, de uma das seguintes maneiras:
[Developer("Joan Smith", "1")]
-or-
[Developer("Joan Smith", "1", Reviewed = true)]
[Developer("Joan Smith", "1")]
-or-
[Developer("Joan Smith", "1", Reviewed = true)]
<Developer("Joan Smith", "1")>
-or-
<Developer("Joan Smith", "1", Reviewed := true)>
O primeiro exemplo mostra o atributo aplicado apenas com os parâmetros nomeados necessários. O segundo exemplo mostra o atributo aplicado com os parâmetros obrigatórios e opcionais.