Come creare un attributo personalizzato in C #


119

Ho provato molte volte ma ancora non sono in grado di capire l'utilizzo degli attributi personalizzati (ho già passato molti collegamenti).

Qualcuno può spiegarmi un esempio molto semplice di un attributo personalizzato con codice?

Risposte:


96

Sebbene il codice per creare un attributo personalizzato sia abbastanza semplice, è molto importante capire quali sono gli attributi:

Gli attributi sono metadati compilati nel tuo programma. Gli attributi stessi non aggiungono alcuna funzionalità a una classe, proprietà o modulo, ma solo dati. Tuttavia, utilizzando la riflessione, è possibile sfruttare questi attributi per creare funzionalità.

Quindi, ad esempio, diamo un'occhiata al Validation Application Block , dalla Enterprise Library di Microsoft . Se guardi un esempio di codice, vedrai:

    /// <summary>
    /// blah blah code.
    /// </summary>
    [DataMember]
    [StringLengthValidator(8, RangeBoundaryType.Inclusive, 8, RangeBoundaryType.Inclusive, MessageTemplate = "\"{1}\" must always have \"{4}\" characters.")]
    public string Code { get; set; }

Dallo snippet sopra, si potrebbe intuire che il codice sarà sempre validato, ogni volta che viene modificato, secondo le regole del Validator (nell'esempio, avere almeno 8 caratteri e al massimo 8 caratteri). Ma la verità è che l'attributo non fa nulla; come accennato in precedenza, aggiunge solo metadati alla proprietà.

Tuttavia, Enterprise Library ha un Validation.Validatemetodo che esaminerà il tuo oggetto e, per ogni proprietà, controllerà se il contenuto viola la regola informata dall'attributo.

Quindi, è così che dovresti pensare agli attributi: un modo per aggiungere dati al tuo codice che potrebbero essere successivamente utilizzati da altri metodi / classi / ecc.


mi piacerà davvero la risposta e specialmente ", ancora una domanda posso mettere la stessa condizione
nell'istruzione

1
@slash: puoi riformularlo? Non ho capito bene la domanda.
Bruno Brant

1
Penso che slash intendesse chiedere la differenza tra l'utilizzo degli attributi e l'inserimento del codice di convalida effettivo all'interno del setter delle proprietà. Risposta: Sebbene sia possibile scrivere codice all'interno del setter per convalidare il valore, l'utilizzo degli attributi da solo non eseguirà la convalida in quanto tale. Gli attributi sono solo "metadati". Un altro codice da qualche altra parte dovrebbe essere interessato agli attributi che usi, leggerli ed eseguire azioni basate su di essi. Un tipico esempio è una libreria di convalida, come menzionato da @BrunoBrant.
romar

10
Non sono sicuro del motivo per cui questa è la risposta accettata. La vera domanda (che è indicizzata anche in Google) è "Come creare un attributo personalizzato in C #". Le risposte non approfondiscono affatto questo argomento. La seconda risposta invece sì.
Drakestar

Penso che la seconda risposta sia più legata alla domanda.
Mohammad Taherian

267

Inizi scrivendo una classe che deriva da Attribute :

public class MyCustomAttribute: Attribute
{
    public string SomeProperty { get; set; }
}

Quindi potresti decorare qualsiasi cosa (classe, metodo, proprietà, ...) con questo attributo:

[MyCustomAttribute(SomeProperty = "foo bar")]
public class Foo
{

}

e infine useresti la riflessione per recuperarlo:

var customAttributes = (MyCustomAttribute[])typeof(Foo).GetCustomAttributes(typeof(MyCustomAttribute), true);
if (customAttributes.Length > 0)
{
    var myAttribute = customAttributes[0];
    string value = myAttribute.SomeProperty;
    // TODO: Do something with the value
}

È possibile limitare i tipi di destinazione a cui applicare questo attributo personalizzato utilizzando l' attributo AttributeUsage:

/// <summary>
/// This attribute can only be applied to classes
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class MyCustomAttribute : Attribute

Cose importanti da sapere sugli attributi:

  • Gli attributi sono metadati.
  • Sono cotti nell'assemblea a fase compilazione, il che ha implicazioni molto serie su come è possibile impostare le loro proprietà. Sono accettati solo valori costanti (noti in fase di compilazione)
  • L'unico modo per avere senso e utilizzare gli attributi personalizzati è usare Reflection . Quindi se non usi la reflection in fase di esecuzione per recuperarli e decorare qualcosa con un attributo personalizzato, non aspettarti che succeda molto.
  • Il tempo di creazione degli attributi non è deterministico. Sono istanziati dal CLR e non hai assolutamente alcun controllo su di essi.

3
Dove, in quale funzione / classe, devo `usare la riflessione per recuperarla`
Hasan A Yousef

@ Hasan A Yousef, ad esempio in Entity Framework c'è l'attributo "Key" che dice al framework: Questa proprietà dovrebbe essere considerata come chiave primaria. Nella creazione di ORM, gli attributi sono molto utili
Parsa

Come si accede a un attributo personalizzato su una proprietà e non su una classe?
Canvas

docs.microsoft.com/en-us/dotnet/standard/attributes/… solo per completezza, questa pagina msdn lo riassume molto bene
Barış Akkurt

Con i generici, è molto, molto più facile ottenere i tipi:var value = typeof(Foo).GetCustomAttributes<MyCustomAttribute>().First().SomeProperty;
jpaugh

27

Utilizzo / copia della grande risposta di Darin Dimitrov , ecco come accedere a un attributo personalizzato su una proprietà e non su una classe:

La proprietà decorata [di classe Foo]:

[MyCustomAttribute(SomeProperty = "This is a custom property")]
public string MyProperty { get; set; }

Recuperarlo:

PropertyInfo propertyInfo = typeof(Foo).GetProperty(propertyToCheck);
object[] attribute = propertyInfo.GetCustomAttributes(typeof(MyCustomAttribute), true);
if (attribute.Length > 0)
{
    MyCustomAttribute myAttribute = (MyCustomAttribute)attribute[0];
    string propertyValue = myAttribute.SomeProperty;
}

Puoi lanciarlo in un ciclo e utilizzare la riflessione per accedere a questo attributo personalizzato anche su ogni proprietà della classe Foo:

foreach (PropertyInfo propertyInfo in Foo.GetType().GetProperties())
{
    string propertyName = propertyInfo.Name;

    object[] attribute = propertyInfo.GetCustomAttributes(typeof(MyCustomAttribute), true);
    // Just in case you have a property without this annotation
    if (attribute.Length > 0)
    {
        MyCustomAttribute myAttribute = (MyCustomAttribute)attribute[0];
        string propertyValue = myAttribute.SomeProperty;
        // TODO: whatever you need with this propertyValue
    }
}

Grazie mille a te, Darin !!


come potremmo estenderlo se non sappiamo quali tipi di attributi esistono su una proprietà? object[] attribute = propertyInfo.GetCustomAttributes(typeof(???), true);Voglio solo iterare su tutti loro e chiamare un metodo m1()di ogni attributo sconosciuto
heyNow

0

La risposta breve è per creare un attributo in c # devi solo ereditarlo dalla classe Attribute, solo questo :)

Ma qui spiegherò in dettaglio gli attributi:

fondamentalmente gli attributi sono classi che possiamo usarli per applicare la nostra logica ad assembly, classi, metodi, proprietà, campi, ...

In .Net, Microsoft ha fornito alcuni attributi predefiniti come Obsolete o Attributi di convalida come ([Required], [StringLength (100)], [Range (0, 999.99)]), inoltre abbiamo tipi di attributi come ActionFilters in asp.net che può essere molto utile per applicare la nostra logica desiderata ai nostri codici (leggi questo articolo sui filtri di azione se sei appassionato di impararlo)

un altro punto, puoi applicare una sorta di configurazione al tuo attributo tramite AttibuteUsage.

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]

Quando decori una classe di attributi con AttributeUsage puoi dire al compilatore c # dove userò questo attributo: lo userò sulle classi, sugli assembly sulle proprietà o su ... e il mio attributo può essere usato più volte su target definiti (classi, assembly, proprietà, ...) o no ?!

Dopo questa definizione degli attributi ti mostrerò un esempio: immagina di voler definire una nuova lezione all'università e di voler consentire solo ad amministratori e master della nostra università di definire una nuova lezione, ok?

namespace ConsoleApp1
{
    /// <summary>
    /// All Roles in our scenario
    /// </summary>
    public enum UniversityRoles
    {
        Admin,
        Master,
        Employee,
        Student
    }

    /// <summary>
    /// This attribute will check the Max Length of Properties/fields
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)]
    public class ValidRoleForAccess : Attribute
    {
        public ValidRoleForAccess(UniversityRoles role)
        {
            Role = role;
        }
        public UniversityRoles Role { get; private set; }

    }


    /// <summary>
    /// we suppose that just admins and masters can define new Lesson
    /// </summary>
    [ValidRoleForAccess(UniversityRoles.Admin)]
    [ValidRoleForAccess(UniversityRoles.Master)]
    public class Lesson
    {
        public Lesson(int id, string name, DateTime startTime, User owner)
        {
            var lessType = typeof(Lesson);
            var validRolesForAccesses = lessType.GetCustomAttributes<ValidRoleForAccess>();

            if (validRolesForAccesses.All(x => x.Role.ToString() != owner.GetType().Name))
            {
                throw new Exception("You are not Allowed to define a new lesson");
            }
            
            Id = id;
            Name = name;
            StartTime = startTime;
            Owner = owner;
        }
        public int Id { get; private set; }
        public string Name { get; private set; }
        public DateTime StartTime { get; private set; }

        /// <summary>
        /// Owner is some one who define the lesson in university website
        /// </summary>
        public User Owner { get; private set; }

    }

    public abstract class User
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
    }


    public class Master : User
    {
        public DateTime HireDate { get; set; }
        public Decimal Salary { get; set; }
        public string Department { get; set; }
    }

    public class Student : User
    {
        public float GPA { get; set; }
    }



    class Program
    {
        static void Main(string[] args)
        {

            #region  exampl1

            var master = new Master()
            {
                Name = "Hamid Hasani",
                Id = 1,
                DateOfBirth = new DateTime(1994, 8, 15),
                Department = "Computer Engineering",
                HireDate = new DateTime(2018, 1, 1),
                Salary = 10000
            };
            var math = new Lesson(1, "Math", DateTime.Today, master);

            #endregion

            #region exampl2
            var student = new Student()
            {
                Name = "Hamid Hasani",
                Id = 1,
                DateOfBirth = new DateTime(1994, 8, 15),
                GPA = 16
            };
            var literature = new Lesson(2, "literature", DateTime.Now.AddDays(7), student);
            #endregion

            ReadLine();
        }
    }


}

Nel mondo reale della programmazione forse non usiamo questo approccio per usare gli attributi e l'ho detto a causa del suo punto educativo nell'uso degli attributi

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.