MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Atributele în .NET

Atributele în .NET reprezintă instrumente speciale care permit încorporarea de metadate suplimentare în asamblări. Atributele pot fi aplicate întregului tip (clasă, interfață etc.) sau anumitor părți ale acestuia (metodă, proprietate etc.). Baza atributelor o constituie clasa System.Attribute, din care derivă toate celelalte clase de atribute. În .NET există multe clase de atribute integrate, dar putem crea și propriile noastre clase de atribute care să definească metadate pentru alte tipuri.

Să presupunem că trebuie să verificăm dacă un utilizator îndeplinește anumite restricții de vârstă. Vom crea un atribut care va stoca pragul de vârstă de la care sunt permise anumite acțiuni:

class AgeValidationAttribute : Attribute
{
   public int Age { get; }
   public AgeValidationAttribute() { }
   public AgeValidationAttribute(int age) => Age = age;
}

În esență, aceasta este o clasă obișnuită, moștenită din System.Attribute. În ea sunt definiți doi constructori: unul cu parametri și unul fără. Constructorul cu parametri al atributului primește un prag de vârstă și îl salvează în proprietate.

Acum vom aplica acest atribut unei clase:

[AgeValidation(18)]
public class Person
{
   public string Name { get; }
   public int Age { get; set; }
   public Person(string name, int age)
   {
       Name = name;
       Age = age;
   }
}

Această clasă Person aplică atributul. Pentru aceasta, numele atributului este specificat între paranteze drepte chiar înainte de definirea clasei. Sufixul Attribute nu este obligatoriu de menționat. Ambele scrieri [AgeValidation(18)] și [AgeValidationAttribute(18)] sunt echivalente.

Dacă constructorul atributului prevede utilizarea unor parametri (public AgeValidationAttribute(int age)), atunci după numele atributului putem specifica valorile pentru parametrii constructorului. În acest caz, se transmite valoarea pentru parametrul age. Astfel, practic, spunem că în AgeValidationAttribute proprietatea Age va avea valoarea 18.

Ca alternativă, putem folosi parametri nominalizați pentru toate proprietățile atributului, dacă clasa atributului are un constructor fără parametri: [AgeValidation(Age = 18)].

Acum să obținem atributul clasei Person și să-l folosim pentru a verifica obiectele acestei clase:

Person tom = new Person("Tom", 35);
Person bob = new Person("Bob", 16);
bool tomIsValid = ValidateUser(tom);    // true
bool bobIsValid = ValidateUser(bob);    // false

Console.WriteLine($"Rezultatul validării pentru Tom: {tomIsValid}");
Console.WriteLine($"Rezultatul validării pentru Bob: {bobIsValid}");

bool ValidateUser(Person person)
{
   Type type = typeof(Person);
   // obținem toate atributele clasei Person
   object[] attributes = type.GetCustomAttributes(false);

   // parcurgem toate atributele
   foreach (Attribute attr in attributes)
   {
       // dacă atributul reprezintă tipul AgeValidationAttribute
       if (attr is AgeValidationAttribute ageAttribute)
           // returnăm rezultatul verificării vârstei
           return person.Age >= ageAttribute.Age;
   }
   return true;
}

class AgeValidationAttribute : Attribute
{
   public int Age { get; }
   public AgeValidationAttribute() { }
   public AgeValidationAttribute(int age) => Age = age;
}

[AgeValidation(18)]
public class Person
{
   public string Name { get; }
   public int Age { get; set; }
   public Person(string name, int age)
   {
       Name = name;
       Age = age;
   }
}

În acest caz, în metoda ValidateUser primim un obiect Person și folosind metoda GetCustomAttributes extragem toate atributele din tipul Person. Apoi, dintre atribute, obținem atributul AgeValidationAttribute, dacă acesta există (pentru că putem să nu-l aplicăm clasei) și verificăm dacă vârsta utilizatorului este permisă.

Dacă utilizatorul trece verificarea de vârstă, returnăm true, altfel returnăm false. Dacă atributul nu este aplicat, returnăm true.

Limitarea aplicării atributului

Cu ajutorul atributului AttributeUsage putem limita tipurile la care poate fi aplicat atributul. De exemplu, dorim ca atributul definit mai sus să poată fi aplicat doar claselor:

[AttributeUsage(AttributeTargets.Class)]
class AgeValidationAttribute : Attribute
{
//....................................
}

Limitarea este definită prin enumerarea AttributeTargets, care poate accepta și alte valori:

  • All: utilizat de toate tipurile
  • Assembly: atributul se aplică la asamblare
  • Constructor: atributul se aplică la constructor
  • Delegate: atributul se aplică la delegat
  • Enum: se aplică la enumerare
  • Event: atributul se aplică la eveniment
  • Field: se aplică la câmpul unui tip
  • Interface: atributul se aplică la interfață
  • Method: se aplică la metodă
  • Property: se aplică la proprietate
  • Struct: se aplică la structură

Prin utilizarea operatorului logic OR, putem combina aceste valori. De exemplu, dacă atributul poate fi aplicat atât claselor, cât și structurilor: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)].

← Lecția anterioară Lecția următoare →