Pisanie atrybutów niestandardowych

Aby zaprojektować atrybuty niestandardowe, nie musisz uczyć się wielu nowych pojęć. Jeśli znasz programowanie obiektowe i wiesz, jak projektować klasy, masz już większość potrzebnej wiedzy. Atrybuty niestandardowe to tradycyjne klasy, które pochodzą bezpośrednio lub pośrednio z System.Attribute klasy. Podobnie jak w przypadku tradycyjnych klas atrybuty niestandardowe zawierają metody, które przechowują i pobierają dane.

Podstawowe kroki prawidłowego projektowania niestandardowych klas atrybutów są następujące:

W tej sekcji opisano każdy z tych kroków i kończy się przykładem atrybutu niestandardowego.

Stosowanie atrybutuUsageAttribute

Deklaracja atrybutu niestandardowego zaczyna się od atrybutu System.AttributeUsageAttribute , który definiuje niektóre kluczowe cechy klasy atrybutów. Można na przykład określić, czy atrybut może być dziedziczony przez inne klasy, czy też elementy, do których można zastosować atrybut. Poniższy fragment kodu pokazuje, jak używać elementu 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

Element AttributeUsageAttribute ma trzy elementy członkowskie, które są ważne podczas tworzenia atrybutów niestandardowych: AttributeTargets, Inherited i AllowMultiple.

Element członkowski AttributeTargets

W poprzednim przykładzie określono, AttributeTargets.All wskazując, że ten atrybut można zastosować do wszystkich elementów programu. Alternatywnie można określić AttributeTargets.Class, wskazując, że atrybut można zastosować tylko do klasy lub AttributeTargets.Method, wskazując, że atrybut można zastosować tylko do metody. Wszystkie elementy programu można oznaczyć jako opis za pomocą atrybutu niestandardowego w ten sposób.

Można również przekazać wiele AttributeTargets wartości. Poniższy fragment kodu określa, że atrybut niestandardowy można zastosować do dowolnej klasy lub metody:

[AttributeUsage(AttributeTargets::Class | AttributeTargets::Method)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Method)>
Public Class SomeOtherClass
    Inherits Attribute
    '...
End Class

Właściwość dziedziczona

Właściwość AttributeUsageAttribute.Inherited wskazuje, czy atrybut może być dziedziczony przez klasy, które pochodzą z klas, do których zastosowano atrybut. Ta właściwość przyjmuje flagę (domyślną true ) lub false . W poniższym przykładzie MyAttribute ma wartość truedomyślną Inherited , natomiast YourAttribute ma Inherited wartość 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

Te dwa atrybuty są następnie stosowane do metody w klasie MyClassbazowej :

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

Na koniec klasa YourClass jest dziedziczona z klasy MyClassbazowej . Metoda MyMethod pokazuje MyAttribute , ale nie 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

Właściwość AllowMultiple

Właściwość AttributeUsageAttribute.AllowMultiple wskazuje, czy na elemecie może istnieć wiele wystąpień atrybutu. Jeśli ustawiono wartość true, wiele wystąpień jest dozwolonych. Jeśli ustawiono wartość false (wartość domyślna), dozwolone jest tylko jedno wystąpienie.

W poniższym przykładzie ma wartość falsedomyślną AllowMultiple typu , a parametr YourAttribute ma wartość true: MyAttribute

//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

Po zastosowaniu MyAttribute wielu wystąpień tych atrybutów tworzy błąd kompilatora. W poniższym przykładzie kodu pokazano prawidłowe użycie YourAttribute elementu i nieprawidłowe użycie elementu 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

Jeśli właściwość AllowMultiple i Inherited właściwość są ustawione na true, klasa dziedziczona z innej klasy może dziedziczyć atrybut i mieć inne wystąpienie tego samego atrybutu zastosowanego w tej samej klasie podrzędnej. Jeśli AllowMultiple ustawiono falsewartość , wartości wszystkich atrybutów w klasie nadrzędnej zostaną zastąpione przez nowe wystąpienia tego samego atrybutu w klasie podrzędnej.

Deklarowanie klasy atrybutów

Po zastosowaniu AttributeUsageAttributepolecenia rozpocznij definiowanie specyfiki atrybutu. Deklaracja klasy atrybutów wygląda podobnie do deklaracji tradycyjnej klasy, jak pokazano w poniższym kodzie:

[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

Ta definicja atrybutu pokazuje następujące kwestie:

  • Klasy atrybutów muszą być zadeklarowane jako klasy publiczne.

  • Zgodnie z konwencją nazwa klasy atrybutów kończy się wyrazem Atrybut. Chociaż nie jest to wymagane, ta konwencja jest zalecana w celu zapewnienia czytelności. Po zastosowaniu atrybutu dołączenie wyrazu Atrybut jest opcjonalne.

  • Wszystkie klasy atrybutów muszą dziedziczyć bezpośrednio lub pośrednio z System.Attribute klasy.

  • W programie Microsoft Visual Basic wszystkie niestandardowe klasy atrybutów muszą mieć System.AttributeUsageAttribute atrybut .

Deklarowanie konstruktorów

Podobnie jak w przypadku tradycyjnych klas atrybuty są inicjowane za pomocą konstruktorów. Poniższy fragment kodu ilustruje typowy konstruktor atrybutu. Ten publiczny konstruktor przyjmuje parametr i ustawia zmienną składową równą jej wartości.

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

Konstruktor można przeciążyć, aby uwzględnić różne kombinacje wartości. Jeśli zdefiniujesz również właściwość dla niestandardowej klasy atrybutów, podczas inicjowania atrybutu można użyć kombinacji nazwanych i pozycyjnych parametrów. Zazwyczaj wszystkie wymagane parametry są definiowane jako pozycyjne i wszystkie parametry opcjonalne jako nazwane. W takim przypadku nie można zainicjować atrybutu bez wymaganego parametru. Wszystkie inne parametry są opcjonalne.

Uwaga

W języku Visual Basic konstruktory klasy atrybutów nie powinny używać argumentu ParamArray .

Poniższy przykład kodu pokazuje, jak można zastosować atrybut używający poprzedniego konstruktora przy użyciu opcjonalnych i wymaganych parametrów. Przyjęto założenie, że atrybut ma jedną wymaganą wartość logiczną i jedną opcjonalną właściwość ciągu.

// 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

Deklarowanie właściwości

Jeśli chcesz zdefiniować nazwany parametr lub łatwo zwrócić wartości przechowywane przez atrybut, zadeklaruj właściwość. Właściwości atrybutu należy zadeklarować jako jednostki publiczne z opisem typu danych, który zostanie zwrócony. Zdefiniuj zmienną, która będzie przechowywać wartość właściwości i skojarzyć ją z metodami get i set . W poniższym przykładzie kodu pokazano, jak zaimplementować właściwość w atrybucie:

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

Przykład atrybutu niestandardowego

Ta sekcja zawiera poprzednie informacje i pokazuje, jak zaprojektować atrybut, który dokumentuje informacje o autorze sekcji kodu. Atrybut w tym przykładzie przechowuje nazwę i poziom programisty oraz informację, czy kod został przejrzyszony. Używa trzech zmiennych prywatnych do przechowywania rzeczywistych wartości do zapisania. Każda zmienna jest reprezentowana przez właściwość publiczną, która pobiera i ustawia wartości. Na koniec konstruktor jest definiowany z dwoma wymaganymi parametrami:

[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

Ten atrybut można zastosować przy użyciu pełnej nazwy , DeveloperAttributelub przy użyciu skróconej nazwy , Developerw jeden z następujących sposobów:

[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)>

W pierwszym przykładzie pokazano atrybut zastosowany tylko z wymaganymi nazwanymi parametrami. W drugim przykładzie przedstawiono atrybut zastosowany zarówno z wymaganymi, jak i opcjonalnymi parametrami.

Zobacz też