• 20 heures
  • Moyenne

Ce cours est visible gratuitement en ligne.

course.header.alt.is_certifying

J'ai tout compris !

Mis à jour le 10/03/2017

Attributs et réflexion

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Alors là, on monte d'un cran. Avec ce chapitre, on rentre encore plus dans les entrailles du C# et des mystères du .NET. 

Nous allons parler ici de deux concepts assez avancés, mais ne vous inquiétez pas, on va rester dans du simple à comprendre. :) 

Les attributs

Dans la liste des choses qui vont vous servir, on peut rajouter les attributs.
Ils sont utilisés pour fournir des informations complémentaires sur une méthode, une classe, variables, etc. Ils vont nous servir régulièrement dans le framework .NET et nous aurons même la possibilité de créer nos propres attributs.

Un attribut est déclaré entre crochets avec le nom de sa classe. Il peut se mettre au-dessus d’une classe, d’une méthode, d’une propriété, etc.
Par exemple, dans le code ci-dessous, je positionne l’attribut Obsolete au-dessus de la déclaration d’une méthode :

[Obsolete("Utilisez plutôt la méthode ToString() pour avoir une représentation de l'objet")]
public void Affiche()
{
    // ...
}

Ici, j’utilise un attribut existant du framework .NET, l’attribut Obsolete qui permet d’indiquer qu’une méthode est obsolète et qu’il ne vaudrait mieux pas l’utiliser. En général, il est possible de fournir une information permettant de dire pourquoi la méthode est obsolète et par quoi il faut la remplacer. Ainsi, si nous tentons d’utiliser cette méthode :

public class Program
{
    static void Main(string[] args)
    {
        new Program().Affiche();
    }

    [Obsolete("Utilisez plutôt la méthode ToString() pour avoir une représentation de l'objet")]
    public void Affiche()
    {
        // ...
    }
}

Le compilateur nous affichera l’avertissement suivant :

'MaPremiereApplication.Program.Affiche()' est obsolète : 'Utilisez plutôt la méthode ToString() pour avoir une représentation de l'objet'

Cet attribut est un exemple, il en existe beaucoup dans le framework .NET et vous aurez l’occasion d’en voir d’autres dans les chapitres suivants.
Remarquons que la classe s’appelle ObsoleteAttribute et peut s’utiliser soit suffixée par Attribute, soit sans, et dans ce cas, ce suffixe est ajouté automatiquement.

Grâce à l’héritage, nous allons nous aussi pouvoir définir nos propres attributs. Il suffit pour cela de dériver de la classe de base Attribute. Par exemple, dans sa forme la plus simple :

public class DescriptionClasseAttribute : Attribute
{
}

Nous pourrons alors utiliser notre attribut sur une classe :

[DescriptionClasse]
public class Chien
{
}

Super, même si ça ne nous sert encore à rien :p .

Problème, j’ai appelé mon attribut DescriptionClasse et je peux quand même l’utiliser sur une méthode :

[DescriptionClasse]
public class Chien
{
    [DescriptionClasse]
    public void Aboyer()
    {
    }
}

Heureusement, il est possible d’indiquer des restrictions sur les attributs en indiquant par exemple qu’il n’est valable que pour les classes. Cela se fait avec … un attribut :D :

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class DescriptionClasseAttribute : Attribute
{
}

Ici par exemple, j’indique que mon attribut n’est valable que sur les classes et qu’il est possible de le mettre plusieurs fois sur la classe. Le code précédent où l’attribut est positionné sur une méthode ne compilera donc plus, avec l’erreur de compilation suivante :

L'attribut 'DescriptionClasse' n'est pas valide dans ce type de déclaration. Il n'est valide que dans les déclarations 'class'.

Ce qui nous convient parfaitement. :)

Il est intéressant de pouvoir fournir des informations complémentaires à notre attribut. Pour cela, on peut rajouter des propriétés, ou avoir une surcharge complémentaire du constructeur, comme on l’a fait pour le message de l’attribut Obsolete :

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class DescriptionClasseAttribute : Attribute
{
    public string Description { get; set; }

    public DescriptionClasseAttribute()
    {
    }

    public DescriptionClasseAttribute(string description)
    {
        Description = description;
    }
}

Ce qui nous permettra d’utiliser notre attribut de ces deux façons :

[DescriptionClasse(Description = "Cette classe correspond à un chien")]
[DescriptionClasse("Elle dérive de la classe Animal")]
public class Chien : Animal
{
}

C’est très bien ces attributs, mais nous ne sommes pas encore capables de les exploiter. Voyons comment le faire.

La réflexion

La réflexion en C# ne donne pas mal à la tête, quoique …
C’est une façon de faire de l’introspection sur nos types de manière à obtenir des informations sur eux, c'est-à-dire sur les méthodes, les propriétés, etc. La réflexion permet également de charger dynamiquement des types et de faire de l’introspection sur les assemblys.
C’est un mécanisme assez complexe et plutôt couteux en termes de temps. Le point de départ est la classe Type qui permet de décrire n’importe quel type. Le type d’une classe peut être obtenu grâce au mot-clé typeof :

Type type = typeof(string);

Cet objet Type nous permet d’obtenir plein d’informations sur nos classes au moment de l’exécution. Par exemple si le type est une classe, c’est-à-dire ni un type valeur, ni une interface, alors le code suivant :

Type type = typeof(string);
Console.WriteLine(type.IsClass);

nous renverra true. En effet, la propriété IsClass permet d’indiquer si le type est une classe.
Nous pouvons aussi obtenir le nom de méthodes grâce à la méthode GetMethods() :

Type type = typeof(string);
foreach (MethodInfo infos in type.GetMethods())
{
    Console.WriteLine(infos.Name);
}

Qui nous donne :

Image utilisateur

Pas très exploitable comme résultat, mais bon c’est pour l’exemple. :) Il y a encore pleins d’infos dans cet objet Type, qui permettent d’obtenir ce que nous voulons.
Cela est possible grâce aux métadonnées qui sont des informations de descriptions des types.

Vous me voyez venir… eh oui, nous allons pouvoir obtenir nos attributs !
Pour connaitre la liste des attributs d’un type, on peut utiliser la méthode statique Attribute.GetCustomAttributes(), en lui passant en paramètre le System.Type qui est hérité de la classe abstraite MemberInfo. Si l’on souhaite récupérer un attribut particulier, on peut le passer en paramètre de la méthode :

Attribute[] lesAttributs = Attribute.GetCustomAttributes(type, typeof(DescriptionClasseAttribute));

Ceci va nous permettre de récupérer les attributs de notre classe et en l’occurrence, d’obtenir leurs description. C’est ce que permet le code suivant :

public class Program
{
    static void Main(string[] args)
    {
        new DemoAttributs().Demo();
    }

}

public class DemoAttributs
{
    public void Demo()
    {
        Animal animal = new Animal();
        Chien chien = new Chien();

        VoirDescription(animal);
        VoirDescription(chien);
    }

    public void VoirDescription<T>(T obj)
    {
        Type type = typeof(T);
        if (!type.IsClass)
            return;
        Attribute[] lesAttributs = Attribute.GetCustomAttributes(type, typeof(DescriptionClasseAttribute));
        if (lesAttributs.Length == 0)
            Console.WriteLine("Pas de description pour la classe " + type.Name + "\n");
        else
        {
            Console.WriteLine("Description pour la classe " + type.Name);
            foreach (DescriptionClasseAttribute attribut in lesAttributs)
            {
                Console.WriteLine("\t" + attribut.Description);
            }
        }
    }
}

Nous commençons par instancier un objet animal et un objet chien. Puis nous demandons leurs descriptions.
La méthode générique VoirDescription() s’occupe dans un premier temps d’obtenir le type de la classe grâce à typeof. On s’octroie une vérification permettant de nous assurer que le type est bien une classe. Ceci permet d’éviter d’aller plus loin car de toute façon, notre attribut ne s’applique qu’aux classes. Puis nous utilisons la méthode GetCustomAttributes pour obtenir les attributs de type DescriptionClasseAttribute. Et s’il y en a, alors on les affiche.

Ce qui donne :

Pas de description pour la classe Animal

Description pour la classe Chien
        Elle dérive de la classe Animal
        Cette classe correspond à un chien

Voilà pour les attributs, vous aurez l’occasion de rencontrer des attributs du framework .NET un peu plus loin dans ce tutoriel, mais globalement, il est assez rare d’avoir à créer soi-même ses attributs.

En ce qui concerne la réflexion, il faut bien comprendre que le sujet est beaucoup plus vaste que ça. Nous avons cependant vu ici un aperçu de ce que permet de faire la réflexion, en illustrant le mécanisme d’introspection sur les attributs.

En résumé

  • Les attributs permettent de rajouter des méta-données sur nos classes, nos méthodes, etc.

  • La réflexion est un mécanisme d'introspection sur les types permettant d'obtenir des informations sur ces derniers lors de l'exécution.

Exemple de certificat de réussite
Exemple de certificat de réussite