Atrybut to znacznik, który służy do przekazywania informacji do środowiska wykonawczego o zachowaniu różnych elementów, takich jak:
klasy, metody, struktury, typy wyliczeniowe czy poszczególne podzespoły naszego programu. Informacje takie mogą zostać zdeklarowane
przy użyciu atrybutów. Atrybuty znajdują się pomiędzy nawiasami kwadratowymi.
Atrybuty służą do dodawania metadanych takich jak instrukcje dla kompilatora, komentarze, opisy metod oraz klas w naszym programie.
Platforma .NET udostępnia dwa rodzaje atrybutów:
atrybuty predefiniowane;
atrybuty niestandardowe.
Definiowanie atrybutów
Składnia definicji atrybutu:
[Atrybut(parametry_wskazujace, wlasciowosci = value, ...)]
element
Nazwa atrybutu i jego wartości podawane są w nawiasach kwadratowych, przed elementem, do którego ten atrybut chcemy zastosować.
Parametry wskazujące określają podstawowe informacje (wskazanie elementów aplikacji do których można dołączyć atrybuty)
a właściowości określają dodatkowe informacje (np. czy atrybuty mogą być dziedziczone).
Predefiniowane atrybuty
Środowisko .NET dostarcza trzy predefiniowane atrybuty:
AtributeUsage;
Conditional;
Obsolete.
AttributeUsage
Atrybut AttributeUsage opisuje sposób użycia innego atrybutu klasy. Określa on typ elementów,
które mogą ten atrybut zastosować.
parametr validon określa elementy aplikacji do której można przypisać atrybuty.
Jest to kombinacja wartości z typu wyliczeniowego AttributeTargets. Domyślna wartość to
AttributeTargets.All;
parametr allowmultiple jest parametrem opcjonalnym i stanowi wartość dla właściowości
AllowMultiple, która jest wartością logiczną określającą czy do danego elementu można
przypisać więcej niż jeden atrybut. Domyślna wartość to false;
parametr inherited jest parametrem opcjonalnym i stanowi wartość dla właściowości
Inherited, która jest wartością logiczną. Jeżeli jest prawdziwa, atrybut jest dziedziczony z
klasy pochodnej. Domyślna wartość to false.
Przykład użycia atrybutu:
using System;
namespace Atrybuty
{
class Program
{
static void Main(string[] args)
{
}
}
// Atrybut, który może być dołączony do
// klasy, konstruktora, pola, właściowości
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Property,
AllowMultiple = true)]
class HelpAttribute : Attribute
{
// Klasa, która używa atrybutu AttributeUsage musi dziedziczyć
// z klasy Attribute
protected string description;
public HelpAttribute(string Description)
{
// this mówi nam, że odwołujemy się do składowej danej klasy
// nie jest to wymagane ale pozwoli jednoznacznie określić
// że chcemy do pola naszej klasy przypisać wartość z konstruktora
this.description = Description;
}
}
[Help("Ta klasa nic nie robi")]
class Przyklad1
{
public Przyklad1()
{
}
// Atrybut ten nie może zostać użyty w tej metodzie gdyż zostało to
// zabronione przy definicji atrybutu AttributeUsage
[Help("Ta metoda nic nie robi")]
public void Metoda1()
{
// I teraz bardzo ważna uwaga dla czytelników
// Atrybuty są ściśle związane z mechanizmem refleksji
// Mechanizm ten pozwala na uzyskiwanie informacji o typie w trakcie kompilacji
// Możemy zatem uzyskać informacje o atrybutach
// Na tym jednak skupimy się w kolejnym rozdziale
}
}
}
Conditional
Atrybut ten oznacza metodę warunkową, której wykonanie będzie zależało od zdefiniowanego identyfikatora.
Powoduje warunkową kompilację wywołań metod w zależności od podania wartości takich jak: Debug
czy Trace. Dzięki takiej kompilacji warunkowej możemy wyświetlać dodatkowe informacje w trakcie
debugowania naszego kodu.
Składnia definicji atrybutu:
[Conditional(
symbol_warunkowy
)]
Przykład użycia atrybutu:
#define DEBUG
using System;
using System.Diagnostics;
namespace AtrybutyConditional
{
class Program
{
static void Main(string[] args)
{
DisplayMessage.Message("Wewnątrz głównej metody");
method1();
Console.ReadKey();
// Wynik działania programu
//Wewnatrz glównej metody
//Wewnatrz metody 1
//Wewnatrz metody 2
}
static void method1()
{
DisplayMessage.Message("Wewnątrz metody 1");
method2();
}
static void method2()
{
DisplayMessage.Message("Wewnątrz metody 2");
}
}
class DisplayMessage
{
[Conditional("DEBUG")]
public static void Message(string message)
{
Console.WriteLine(message);
}
}
}
Obsolete
Atrybut ten oznacza część programu, która nie powinna być dłużej używana. Prosty przykład dotyczy powstania nowej metody przy konieczności
pozostawienia starej w naszym kodzie. Tą drugą możemy oznaczyć jako przestarzałą
(Obsolete) przez wyświetlenie komunikatu informującego o tym, że nowa metoda powinna być używana.
parametr informacja opisuje czemu dana metoda jest przestarzała i co powinno być używane
zamiast niej;
parametr czy_traktowac_uzycie_jako_blad jest wartością logiczną. Jeżeli wartość logiczna
jest prawdziwa kompilator powinien traktować używanie tej metody jako błąd. Domyślne zachowanie kompilatora polega na
wygenerowaniu ostrzeżenia – warunek logiczny jest fałszywy.
Przykład użycia atrybutu:
using System;
namespace AtrybutyObsolete
{
class Program
{
static void Main(string[] args)
{
// Taki program się nie kompiluje
// Otrzymujemy poniższy błąd:
// 'Program.OldMethod()' is obsolete: 'Proszę nie używać tej metody, nowa wersja: NewMethod()'
OldMethod();
}
[Obsolete("Proszę nie używać tej metody, nowa wersja: NewMethod()", true)]
static void OldMethod()
{
Console.WriteLine("Stara, nieużywana metoda");
}
static void NewMehtod()
{
Console.WriteLine("Nowa wersja metody");
}
}
}
Tworzenie niestandardowych atrybutów
.NET Framework pozwala na tworzenie niestandardowych atrybutów, które mogą być używane do przechowywania różnych informacji i pobrane w trakcie wykonywania programu. Informacje te mogą być związane z jakimkolwiek elementem w zależności od kryterium projektowego i potrzeb aplikacji.
Tworzenie i używanie niestandardowych atrybutów związane jest z czterema poniższymi krokami:
deklaracja atrybutu niestandardowego;
konstruowanie atrybutu niestandardowego;
użycie atrybutu na docelowym elemencie programu;
dostęp do atrybutu przez mechanizm refleksji.
Deklaracja atrybutu niestandardowego
Nowy niestandardowy atrybut powinien dziedziczyć z klasy System.Attribute:
[AttributeUsage(AttributeTargets.Class |
AttributeTargets.Constructor |
AttributeTargets.Method |
AttributeTargets.Field |
AttributeTargets.Property,
AllowMultiple = true)]
public class DebugInfo : Attribute
{
}
W powyższym przykładzie zdefiniowaliśmy atrybut o nazwie DebugInfo.
Konstruowanie atrybutu niestandardowego
Przygotujmy atrybut niestandardowy, który będzie przechowywał informacje o naszym programie. Będzie on przechowywał następujące
informacje:
Nasz atrybut DebugInfo będzie posiadał trzy prywatne właściwości do przechowywania trzech
pierwszych informacji oraz właściwość publiczną do przechowywania komentarza programisty. Stąd, numer błędu, imię programisty
oraz data przeglądu kodu są parametrami klasy DebugInfo a komentarz jest opcjonalny.
public class DebugInfo : Attribute
{
// pola prywatne
private int codeNumber;
private string developerName;
private string lastReviewData;
public string message;
public DebugInfo(int code, string dev, string d)
{
this.codeNumber = code;
this.developerName = dev;
this.lastReviewData = d;
}
// właściwości, które dają nam dostęp do pól prywatnych
// ich użycie będzie potrzebne podczas korzystania z mechanizmu refleksji
// ale o tym w kolejnym rozdziale
public int CodeNumber
{
get
{
return codeNumber;
}
}
public string DeveloperName
{
get
{
return developerName;
}
}
public string LastReviewData
{
get
{
return lastReviewData;
}
}
}
Użycie atrybutu
Atrybutu używa się poprzez użycie go bezpośrednio nad elementem docelowym: