Comment créer un attribut personnalisé en C #


119

J'ai essayé beaucoup de fois mais je ne suis toujours pas en mesure de comprendre l'utilisation des attributs personnalisés (j'ai déjà parcouru de nombreux liens).

Quelqu'un peut-il s'il vous plaît m'expliquer un exemple très basique d'un attribut personnalisé avec du code?

Réponses:


96

Bien que le code pour créer un attribut personnalisé soit assez simple, il est très important que vous compreniez ce que sont les attributs:

Les attributs sont des métadonnées compilées dans votre programme. Les attributs eux-mêmes n'ajoutent aucune fonctionnalité à une classe, une propriété ou un module - juste des données. Cependant, en utilisant la réflexion, on peut exploiter ces attributs afin de créer des fonctionnalités.

Alors, par exemple, regardons le bloc d'application de validation , de la bibliothèque d'entreprise de Microsoft . Si vous regardez un exemple de code, vous verrez:

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

À partir de l'extrait ci-dessus, on peut deviner que le code sera toujours validé, chaque fois qu'il sera modifié, conformément aux règles du validateur (dans l'exemple, avoir au moins 8 caractères et au plus 8 caractères). Mais la vérité est que l'attribut ne fait rien; comme mentionné précédemment, il ajoute uniquement des métadonnées à la propriété.

Cependant, la bibliothèque d'entreprise a un Validation.Validate méthode qui examinera votre objet, et pour chaque propriété, elle vérifiera si le contenu enfreint la règle informée par l'attribut.

Donc, c'est ainsi que vous devriez penser aux attributs - un moyen d'ajouter des données à votre code qui pourraient être utilisées plus tard par d'autres méthodes / classes / etc.


vais-je vraiment aimer la réponse et spécialement ", une autre question que je peux mettre la même condition dans la déclaration d'ensemble du code ci-dessus, alors en quoi il est différent des attributs,
slash shogdhe

1
@slash: Pouvez-vous reformuler cela? Je n'ai pas bien compris la question.
Bruno Brant le

1
Je pense que slash voulait poser des questions sur la différence entre l'utilisation d'attributs et l'insertion du code de validation réel dans le setter de propriétés. Réponse: Bien que l'écriture de code à l'intérieur du setter puisse être effectuée pour valider la valeur, l'utilisation d'attributs seuls n'effectuera pas la validation en tant que telle. Les attributs ne sont que des "méta-données". Un autre code ailleurs devrait s'intéresser aux attributs que vous utilisez, les lire et effectuer des actions basées sur eux. Un exemple typique est une bibliothèque de validation, comme @BrunoBrant l'a mentionné.
romar

10
Je ne sais pas pourquoi c'est la réponse acceptée. La vraie question (qui est également indexée dans Google) est "Comment créer un attribut personnalisé en C #". Les réponses ne plongent pas du tout dans ce sujet. La deuxième réponse fait, d'autre part.
Drakestar

Je pense que la deuxième réponse est plus liée à la question.
Mohammad Taherian

267

Vous commencez par écrire une classe qui dérive de Attribute :

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

Ensuite, vous pouvez décorer n'importe quoi (classe, méthode, propriété, ...) avec cet attribut:

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

}

et enfin vous utiliseriez la réflexion pour le récupérer:

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
}

Vous pouvez limiter les types de cibles auxquels cet attribut personnalisé peut être appliqué à l'aide de l' attribut AttributeUsage :

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

Choses importantes à savoir sur les attributs:

  • Les attributs sont des métadonnées.
  • Ils sont intégrés à l'assemblage au moment de la compilation, ce qui a des implications très sérieuses sur la façon dont vous pouvez définir leurs propriétés. Seules les valeurs constantes (connues au moment de la compilation) sont acceptées
  • La seule façon de donner un sens et d'utiliser des attributs personnalisés est d'utiliser Reflection . Donc, si vous n'utilisez pas la réflexion au moment de l'exécution pour les récupérer et décorer quelque chose avec un attribut personnalisé, ne vous attendez pas à grand chose.
  • Le moment de la création des attributs est non déterministe. Ils sont instanciés par le CLR et vous n'en avez absolument aucun contrôle.

3
Où, dans quelle fonction / classe, dois-je utiliser la réflexion pour le récupérer?
Hasan A Yousef

@Hasan A Yousef, par exemple dans Entity Framework, il y a un attribut "Key" qui indique au framework: Cette propriété doit être considérée comme une clé primaire. Lors de la création d'ORM, les attributs sont très utiles
Parsa

Comment accéder à un attribut personnalisé sur une propriété et non sur une classe?
Toile

docs.microsoft.com/en-us/dotnet/standard/attributes/ ... juste pour être complet, cette page msdn le résume très bien
Barış Akkurt

Avec les génériques, il est beaucoup, beaucoup plus facile d'obtenir les types:var value = typeof(Foo).GetCustomAttributes<MyCustomAttribute>().First().SomeProperty;
jpaugh

27

En utilisant / Copiant la grande réponse de Darin Dimitrov, voici comment accéder à un attribut personnalisé sur une propriété et non sur une classe:

La propriété décorée [de classe Foo]:

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

Récupérer:

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;
}

Vous pouvez lancer ceci dans une boucle et utiliser la réflexion pour accéder à cet attribut personnalisé sur chaque propriété de la classe Foo, ainsi:

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

Grand merci à toi, Darin !!


comment étendre cela si nous ne savons pas quels types d'attributs existent sur une propriété? object[] attribute = propertyInfo.GetCustomAttributes(typeof(???), true);Je veux juste les parcourir tous et appeler une méthode m1()de chaque attribut inconnu
heyNow

0

La réponse courte est pour créer un attribut en c #, il vous suffit de l'hériter de la classe Attribute, juste ceci :)

Mais ici, je vais expliquer les attributs en détail:

fondamentalement, les attributs sont des classes que nous pouvons les utiliser pour appliquer notre logique aux assemblys, classes, méthodes, propriétés, champs, ...

Dans .Net, Microsoft a fourni des attributs prédéfinis tels que des attributs obsolètes ou de validation tels que ([Obligatoire], [StringLength (100)], [Range (0, 999.99)]), ainsi que des types d'attributs comme ActionFilters dans asp.net qui peut être très utile pour appliquer notre logique souhaitée à nos codes (lisez cet article sur les filtres d'action si vous êtes passionné de l'apprendre)

un autre point, vous pouvez appliquer une sorte de configuration sur votre attribut via AttibuteUsage.

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

Lorsque vous décorez une classe d'attribut avec AttributeUsage, vous pouvez dire au compilateur c # où je vais utiliser cet attribut: je vais l'utiliser sur des classes, sur des assemblys sur des propriétés ou sur ... et mon attribut est autorisé à utiliser plusieurs fois sur des cibles définies (classes, assemblages, propriétés, ...) ou pas?!

Après cette définition des attributs, je vais vous montrer un exemple: imaginez que nous voulons définir une nouvelle leçon à l'université et que nous voulons permettre uniquement aux administrateurs et aux maîtres de notre université de définir une nouvelle leçon, d'accord?

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();
        }
    }


}

Dans le monde réel de la programmation, nous n'utilisons peut-être pas cette approche pour utiliser les attributs et je l'ai dit en raison de son intérêt pédagogique dans l'utilisation des attributs

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.