Version imprimable       Envoyer     
Cliquez pour évaluer et commenter
MSDN
MSDN Library
Articles Techniques
Visual Studio 2005
Visual Basic
C#
 Une utilisation sécurisée de la Ref...

  Passer à l'affichage pour faible bande passante
Une utilisation sécurisée de la Reflection
Paru le 10 avril 2007

Article rédigé par Mitsuru Furuta

Je vais traiter d’un sujet un peu particulier qui concerne la « Reflection » de .Net. L’idée provient du blog très intéressant d’un MVP argentin que je suis depuis plus d’un an (http://clariusconsulting.net/blogs/kzu/archive/2006/07/06/TypedReflection.aspx ).

Je vais tenter de vous le vulgariser et de le résumer.

Depuis son origine, .Net offre une API d’accès aux métadonnées de définition de type appelée « Reflection ». Cette API offre de nombreuses possibilités et est intensivement utilisée par de nombreux utilitaires, frameworks, composants et aussi par le framework lui-même.

Parmi les usages classiques, elle peut être utilisée afin de :
- Rendre un comportement dynamique par rapport à un type donné.
(ex : if (t = typeof(Button))
- Implémenter un pattern basé sur une syntaxe particulière (ex : apporter un comportement particulier à toutes les propriétés dont le nom fini par « Changed ».
- Définir un délégué générique de type (nom de méthode + listes des paramètres).
- Créer dynamiquement des instances dans des factory d’objets. (Activator.CreateInstance)

Un danger subsiste cependant quant à son utilisation. En effet, via cette API, les types, les noms de méthodes, les propriétés sont déterminés par leur nom sous forme de chaînes de caractères. Cette possibilité de passer de valeurs texte à des informations de types compilées apporte beaucoup de souplesse et rend le code très dynamique. Le danger pour le développeur est d’utiliser des chaines de caractères qui ne seront bien évidemment pas vérifiées à la compilation.

En effet, le code suivant compile même si CompanyName n’appartient pas à la classe Customer :

        typeof(Customer).GetProperty("CompanyName");
      

Alors que bien évidemment, ce code ne compilera pas si CompanyName n’appartient pas à la classe Customer :

        string s = customer1.CompanyName;
      

Si nous utilisons intensivement la « Reflection » et que nous avons mal écrit le nom des propriétés par exemple ou que le nom des propriétés a évolué, nous prenons le risque d’avoir de potentielles erreurs à l’exécution. Ce problème est évidemment commun avec tous les langages non typés ou autorisant le « late binding ».

Dans notre cas, nous utilisons C# car nous lui préférons sa syntaxe plus stricte et son code plus vérifiable à la compilation. Comment donc faire de la « Reflection » de manière sécurisée ?

C# 3.0 qui sortira avec Orcas, la prochaine version de Visual Studio, offre un nouveau concept très intéressant : les expressions lambda. Ces expressions permettent de faire de la méta-programmation sur les expressions C# classique. Cela signifie que nous allons pouvoir analyser le langage pour en faire autre chose. Dans notre cas, nous allons analyser une expression pour atteindre la PropertyInfo d’une propriété.

L’idée est d’utiliser une expression au lieu d’une chaine de caractère pour définir la propriété que nous ciblons.

Le programme est très court, découvrons-le ensemble :

class Program {
   static void Main(string[] args) {
      PropertyInfo pi =
      ReflectionHelper<Process>.GetProperty(p => p.ProcessName);
   }
}

public static class ReflectionHelper<T>{
   public static PropertyInfo GetProperty(Expression<Func<T, object>> selector) {
      Expression e = selector.Body;
      while (!((e is MemberExpression) && 
        (e.NodeType == ExpressionType.MemberAccess) && 
        ((e as MemberExpression).Member is PropertyInfo))) {
            if ((e is UnaryExpression) && (e.NodeType == ExpressionType.Cast))
              e = (e as UnaryExpression).Operand;
            else
              throw new ArgumentException();
        }

        return (e as MemberExpression).Member as PropertyInfo;
   }
}

      

Le développeur peut désormais utiliser

        ReflectionHelper<Process>.GetProperty(p => p.ProcessName);
      

Au lieu de

        typeof(Customer).GetProperty("CompanyName");
      

Les avantages sont évidents:

- Si ProcessName est mal écrit ou si tout simplement ProcessName n’existe pas dans la classe Process, nous avons alors une erreur à la compilation puisque même si p.ProcessName devient une expression lambda, il faut en tout premier lieu que le code soit correct pour le compilateur C# avant qu’il le transforme en expression !
- Il n’y a plus de crainte à utiliser une telle ligne dans tout l’ensemble du programme. Si jamais on décide de renommer ProcessName, les erreurs de compilations nous indiqueront immédiatement l’impact sur notre code et les lignes à modifier.
- Avec Orcas, on aura même l’intellisense pour piocher notre propriété ! (c’est déjà le cas dans les CTP) !

Vivement Orcas !

PS : le code source attaché demande l’installation de la CTP de Mai de Linq qui s’installe avec VS2005. Le code étant très court, il est très facilement adaptable sur les CTP actuelles.


Liens utiles
© 2009 Microsoft Corporation. Tous droits réservés. Conditions d'utilisation  |  Marques  |  Confidentialité
Page view tracker