Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Améliorer le debug sous VS avec les proxy de classes

Visual Studio est certainelement l'IDE le plus complet qu'on puisse rêver et au-delà de tout ce qu'il offre "out of the box" il est possible de lui ajouter de nombreux add-ins (gratuits ou payants) permettant de l'adapter encore plus à ses propres besoins. Ainsi vous connaissez certainement les "gros" add-ins comme Resharper dont j'ai parlé ici quelque fois ou GhostDoc qui écrit tout seul la doc des classes. Vous connaissez peut-être les add-ins de debogage permettant d'ajouter vos propres visualisateurs personnalisés pour le debug. Mais vous êtes certainement moins nombreux à connaître les proxy de classes pour le debug (Debugger Type Proxy).

A quoi cela sert-il ?

Tout d'abord cela n'a d'intérêt qu'en mode debug. Vous savez que lorsque vous placez un point d'arrêt dans votre code le debugger de VS vous permet d'inspecter le contenu des variables. C'est la base même d'un bon debugger.

La classe à deboguer 

Supposons la classe Company suivante décrivant une société stockée dans notre application. On y trouve trois propriétés, le nom de la société Company, l'ID de la société dans la base de données et la date de dernière facturation LastInvoice :

public class Customer
        {
            private string company;
            public string Company
            {
                get { return company; }
                set { company = value; }
            }
 
            private int id;
            public int ID
            {
                get { return id; }
                set { id = value; }
            }
 
            private DateTime lastInvoice;
            public DateTime LastInvoice
            {
                get { return lastInvoice; }
                set { lastInvoice = value; }
            }
        }

 

Le debug "de base" 

Supposons maintenant que nous placions un point d'arrêt dans notre application pour examiner le contenu d'une variable de type Customer, voici que nous verrons :

Le debugger affiche toutes les valeurs connues pour cette classe, les propriétés publiques comme les champs privés. Il n'y a que 3 propriétés et 3 champs dans notre classe, imaginez le fatras lorsqu'on affiche le contenu d'une instance créée depuis une classe plus riche ! Surtout qu'ici, pour tester notre application, ce dont nous avons besoin immédiatement ce sont juste deux informations claires : le nom et l'ID de la société et surtout le nombre de jours écoulés depuis la dernière facture. Retrouver l'info parmi toutes celles affichées, voire faire un calcul à la main pour celle qui nous manque, c'est transformer une session de debug qui s'annonçait plutôt bien en un véritable parcours du combattant chez les forces spéciales !

Hélas, dans la classe Customer ces informations sont soit éparpillées (nom de société et ID) soit inexistantes (ancienneté en jours de la dernière facture).

Il existe bien entendu la possibilité de créer un visualisateur personnalisé pour la classe Customer et de l'installer dans les plug-ins de Visual Studio. C'est une démarche simple mais elle réclame de créer une DLL et de la déployer sur la machine. Cette solution est parfaite en de nombreuses occasions et elle possède de gros avantages (réutilisation, facilement distribuable à plusieurs développeurs, possibilité de créer un "fiche" complète pour afficher l'information etc).

Mais il existe une autre voie, encore plus simple et plus directe : les proxy de types pour le debugger.

Un proxy de type pour simplifier

A ce stade du debug de notre application nous avons vraiment besoin du nombre de jours écoulés depuis la dernière facture et d'un moyen simple de voir immédiatement l'identification de la société. Nous ne voulons pas créer un visualisateur personnaliser, mais nous voulons tout de même une visualisation personnalisée...

Regardons le code de la classe suivante :

public class CustomerProxy
        {
            private Customer cust;
 
            public CustomerProxy(Customer cust)
            {
                this.cust = cust;
            }
 
            public string FullID
            {
                get { return cust.Company + " (" + cust.ID + ")"; }
            }
 
            public int DaysSinceLastInvoice
            {
                get { return (int) (DateTime.Now - cust.LastInvoice).TotalDays; }
            }
        }

 

la classe CustomerProxy est très (très) simple : elle possède un constructeur prenant en paramètre une instance de la classe Customer puis elle expose deux propriétés en read only : FullID qui retourne le nom de la société suivi de son ID entre parenthèses, et le nombre de jours écoulés depuis la dernière facture.

Nota: Ce code de démo ne contient aucun test... dans la réalité vous y ajouterez des tests sur null pour éviter les exceptions si l'instance passée est nulle, bien entendu.

Vous allez me dire, c'est très joli, ça fait une classe de plus dans mon code, et comment je m'en sers ? je ne vais pas modifier tout mon code pour créer des instances de cette classe cela serait délirant !

Vous avez parfaitement raison, nous n'allons pas créer d'instance de cette classe, nous n'allons pas même modifier le code de l'application en cours de debug (ce qui serait une grave erreur... modifier ce qu'on test fait perdre tout intérêt au test). Non, nous allons simplement indiquer au framework .NET qu'il utilise notre proxy lorsque VS passe en debug... Un attribut à ajouter à la classe originale Customer, c'est tout :

#if (DEBUG)
        [System.Diagnostics.DebuggerTypeProxy(typeof(CustomerProxy))]
#endif
        public class Customer
        {
            //...
        }

Vous remarquerez que pour faire plus "propre" j'ai entouré la déclaration de l'attribut dans un #if DEBUG, cela n'est absolument pas obligatoire, j'ai fait la même chose autour du code de la classe proxy. De ce fait ce code ne sera pas introduit dans l'application en mode Release. Je vous conseille cette approche malgré tout.

Et c'est fini !

Le proxy en marche

Désormais, lorsque nous sommes en debug que nous voulons voir le contenu d'une instance de la classe Customer voici ce que Visual Studio nous affiche :

 

Vous remarquez immédiatement que le contenu de l'instance de la classe Customer n'est plus affiché directement mais en place et lieu nous voyons les deux seules propriétés "artificielles" qui nous intéressent : le nom de société avec son ID, et le nombre de jours écoulés depuis la dernière facture. Fantastique non ? !

"Et si je veux voir le contenu de Customer malgré tout ?" ... Je m'attendais à cette question... Regardez sur l'image ci-dessus, oui, là, le petit "plus" dans un carré, "Raw View"... Cliquez sur le plus et vous aurez accès au même affichage de l'instance de Customer qu'en début d'article (sans le proxy) :

 

Conclusion

Si ça c'est pas de la productivité et de la customisation aux petits oignons alors je suis à court d'arguments ! :-)

Bon debug !

... Et Stay Tuned !

le projet de test : DebugProxy.zip (5,55 kb)

Touchless : Microsoft Surface pour tous ! (un SDK open source)

Surface, vous connaissez ? Bien sur... C'est cette table que Microsoft a créée et qui reconnaît les objets qu'on pose dessus, qui permet de manipuler l'interface avec les mains, un doigt, etc... Le parfait objet que tout geek rêve d'avoir dans son salon !

Mais voilà Surface, c'est cher. Et c'est à peine diffusé dans certains casinos ou grandes chaînes d'hôtels aux US. Avant de pouvoir s'en payer une à la maison, il faudra attendre un peu...

Alors pourquoi ne pas se fabriquer sa propre Surface, voire un mur graphique (le principe de Surface se décline en table, en tableau mural et il existe même une version prototype en sphère !) ?

Là, ce n'est plus tant la question du prix mais celle du logiciel de commande et du matériel nécessaire... Pas tout à fait ! En effet, grâce à Touchless vous aussi vous pouvez jouez à faire bouger des objets virtuels au doigt et à l'oeil et pour pas cher !

Touchless est un projet Open Source sur CodePlex qui offre un SDK et une démo de celui-ci. A l'aide d'une simple WebCam (comme celle que vous avez chez vous ou intégrée dans votre portable) il devient possible de manipuler des objets virtuels avec les mains ou plutôt avec des objets réels que vous avez fait reconnaitre à Touchless. Par exemple votre tasse de café peut devenir un feutre : déplacez la en l'air pour écrire sur l'écran. Enregistrez deux objets et vous pouvez jouez à Pong, chacun controlant un pad, ou bien redimensionner une image par les coins opposés comme avec Surface...

Gros délire de geek ? C'est une évidence. Projet d'avenir ? ça ne fait aucun doute non plus.

C'est gratuit, ouvert, créez votre propre "Surface" pour pas un copek grâce à votre WebCam et à Touchless !

Bon Fun !

 

Créer des documents XAML directement depuis Word 2007

XAML, le langage de description graphique de WPF et Silverlight, est un format XML offrant une grande souplesse pour représenter aussi bien des graphismes en 3D que des fenêtres Windows et leur contenu en passant par les animations, les sites Web ou les flux vidéo et j'en oublie des tonnes... Tout comme HTML il est possible de taper les balises à la main. C'est formateur mais peu productif !

Je me rappelle d'une époque où un "vrai" développeur Web, "un dur, un tatoué", se devait de développer tout un site en HTML uniquement avec le bloc-notes, se servir de DreamWeaver ou autres logiciels de mise en page dédié était un truc de fillette ("les durs, les tatoués" utilisent bien entendu d'autres mots qui tombent sous le coup de la censure anti homophobie...). On retrouve ce réflexe de geek autiste avec toute nouvelle technologie et certains aujourd'hui mettent un point d'honneur à faire une application Silverlight ou WPF en tapant tout le XAML à la main... Même si l'aspect formateur de la chose que je soulignai plus haut possède un certain attrait, cette démarche est loin d'être la plus productive. Il existe en effet des logiciels comme Expression Blend ou même VS 2008 qui offre une approche visuelle de XAML, ce qui est le minimum pour une technologie justement basée sur le visuel !

De fait, qu'il s'agisse d'une application Silverlight ou d'une application desktop WPF ou XBap, il arrive souvent qu'on ait du texte à présenter. Et quitte à écrire des tartines à l'écran, autant que cela soit gracieux...

C'est là que ça se complique... Car bien entendu ni Blend ni VS 2008 ne sont des traitements de texte...

On se demande alors s'il n'y aurait pas une façon élégante d'utiliser Word puis de récupérer le document en XAML, bout de code qu'il n'y aurait plus qu'à coller dans son application.

Si, c'est possible ! ((c) Hassan Céhef, Les Nuls).

En utilisant Word 2007 et le plugin Word 2007 XAML Generator dont le code binaire et le code source .NET sont téléchargeables ici.

Ce plugin pour Word 2007 est intéressant à plus d'une titre. Outre sa fonction première qui est bien utile, le code source illustre la façon d'écrire des plugins pour Word 2007 ce qui donnera peut-être des envies à certains d'entre vous !

Partons d'un document Word (on voit l'onglet XAML, non affiché sur cette capture, en fin de ligne de la barre d'outils) :

 

Une fois le plugin installé, en appelant le menu "enregistrer sous" nous trouvons une option "XAML" (je vous fait grâce de la copie d'écran de ce menu). Il n'y a plus qu'à donner un nom de fichier et à valider. Nous voici maintenant en possession d'une fichier XAML qui contient notre document Word sous la forme d'un FlowDocument si on a choisi une exportation WPF ou un TextbBlock pour Silverlight.

Pour visualiser le résultat rapidement je vous conseille l'excellent freeware Kaxaml. La capture ci-dessous montre dans sa partie supérieure le FlowDocument contenant notre document Word "xamelisé" et, dans sa partie inférieure, le code XAML créé par le plugin. Une fois ce dernier copié/collé dans votre application vous aurez l'assurance d'un visuel de grande qualité. Tout d'abord le texte sera contrôlé (orthographe et grammaire de Word) limitant les coquilles (pas comme sur ce blog pour lequel je n'ai pas de correcteur!), mais il disposera aussi d'une mise en page riche avec des styles cohérents. Enfin, sa représentation à l'écran sera conforme à l'idée qu'on se fait d'une texte propre et professionnel affiché par une application de type WPF ou Silverlight.  Que des avantages donc.

 

On notera que le plugin affiche aussi une barre d'outil dans Word 2007 permettant de régler certains paramètres et d'effectuer le choix entre une exportation WPF ou Silverlight (qui utilise un TextBlock au lieu d'un FlowDocument). La fonction de preview qui est aussi disponible dans la barre d'outils est particulièrement appréciable.

Voici le FlowDocument intégré dans une fenêtre WPF:

Savoir faire fonctionner tous les outils ensemble pour produire des applications riches est une grande force, disposer de petits logiciels ou plugins tels que Word 2007 XAML Generator permet de combler le gap entre certains de ces outils, de faire un pont. Un pont c'est bête, parfois juste une petite planche posée au-dessus d'une tranchée de chantier. Mais qu'est-ce que ça change la vie !

Bon Dev

... Et Stay Tuned !

Class Helper, LINQ et fichiers CSV

C# 3.0 a apporté des nouveautés syntaxiques qui ne se bornent pas à être seulement des petits suppléments qu'on peut ignorer, au contraire il s'agit de modifications de premier plan qui impactent en profondeur la façon d'écrire du code si on sait tirer partie de ces nouveautés. J'en ai souvent parlé, au travers de billets de ce blog ou d'articles comme celui décrivant justement les nouveautés syntaxiques de C#3.0. Je ne vais donc pas faire de redites, je suppose que vous connaissez maintenant tous ces ajouts au langage. Ce que je veux vous montrer c'est qu'en utilisant correctement C# 3.0 on peut écrire un code incroyable concis et clair pour résoudre les problèmes qui se posent au quotidien au développeur.

Le problème à résoudre

Vous recevez un fichier CSV, disons le résultat d'une exportation depuis un soft XY des ventes à l'export de votre société. Vous devez rapidement ajouter la possibilité d'importer ces données dans l'une de vos applications mais après les avoir interprétées, triées, voire filtrées.

La méthode la plus simple

Rappel : un fichier CSV est formé de lignes comportant plusieurs champs séparés par des virgules. Les champs peuvent être délimités par des double quotes.

A partir de cette description voyons comment avec une requête LINQ nous pouvons lire les données, les filtrer, les trier et les mettre en forme. Le but ici sera de générer en sortie une chaîne par enregistrement, chaîne contenant des champs "paddés" par des espaces pour créer un colonnage fixe. On tiendra compte des lignes débutant par le symbole dièse qui sont considérées comme des commentaires.

   1:  string[] lines = File.ReadAllLines("Export Sales Info-demo.csv");
   2:  var t1 = lines
   3:      .Where(l => !l.StartsWith("#"))
   4:      .Select(l => l.Split(','))
   5:      .OrderBy(items=>items[0])
   6:      .Select(items => String.Format("{0}{1}{2}",
   7:          items[1].PadRight(15),
   8:          items[2].PadRight(30),
   9:          items[3].PadRight(10)));
  10:  var t2 = t1
  11:      .Select(l => l.ToUpper());
  12:  foreach (var t in t2.Take(5))
  13:      Console.WriteLine(t);

La sortie (avec le fichier exemple fourni) sera :

SAN JOSE       CITY CENTER LODGE             CA
SAN FRANCISCO  KWIK-E-MART                   CA
SEATTLE        LITTLE CORNER SWEETS          WA
SEATTLE        LITTLE CORNER SWEETS          WA
IRVINE         PENNY TREE FOODS CORPORATION  CA
 

Cette méthode, très simple, ne réclame rien d'autre que le code ci-dessus. La solution est applicable à tout moment et s'adapte facilement à toute sorte de fichiers sources. Elle possède malgré tout quelques faiblesses. Par exemple les champs contenant des doubles quotes ou les champs mal formés risquent de faire échouer la séquence. Dans certains cas cela sera inacceptable, dans d'autres on pourra parfaitement protéger la séquence dans un bloc try/catch et rejeter les fichiers mal formés. Une fois encore il ne s'agit pas ici de montrer un code parfaitement adapté à un problème précis, mais bien de montrer l'esprit, la façon d'utiliser C# 3.0 pour résoudre des problèmes courants. 

Expliquons un peu ce code :

la ligne 1 charge la totalité du fichier CSV dans un tableau de strings. La méthode peut sembler "brutale" mais en réalité elle est souvent très acceptable car de tels fichiers dépassent rarement les quelques dizaines ou centaines de Ko et la RAM de nos machines modernes n'impose en rien une lecture bufferisée, tout peut tenir d'un bloc en mémoire sans le moindre souci. Cela nous arrange ici il faut l'avouer mais l'utilisation d'un flux bufferisé resterait parfaitement possible.

Nous disposons maintenant d'un tableau de chaînes, chacune étant formatée en CSV. La première requête LINQ (variable "t1" en ligne 2) fait l'essentiel du travail :

  • gestion des commentaires (ligne 3)
  • décomposition des champs (ligne 4)
  • tri sur l'un des champs (ligne 5)
  • génération d'une sortie formatée (lignes 6 à 9)

Ce qui est merveilleux ici c'est que nous avons en quelques lignes un concentré de ce qu'est une application informatique : acquisition de données, traitement de ces données, production de sorties exploitables par un être humain ou une autre application. En si peu de lignes nous avons réalisé ce qui aurait nécessité une application entière avec les langages de la génération précédente comme C++ ou Delphi. C'est bien ce bond en avant que représente C# 3.0 qui est ici le vrai sujet du billet.

Une méthode plus complète

La séquence que nous avons vu plus haut répond au problème posé. Elle possède quelques lacunes. Celles liées à sa trop grande simplicité (certains cas du parsing CSV ne sont pas correctement pris en compte) et celles liées à sa forme : c'est un bout de code qu'il faudra copier/coller pour le réutiliser et qui viendra "polluer" nos requêtes LINQ les rendant plus difficiles à lire.

Si ces lacunes sont acceptables dans certains cas (règlement ponctuel d'un problème ponctuel) dans d'autres cas on préfèrera une approche plus facilement réutilisable. Notamment si nous sommes amenés à traiter plus ou moins souvent des fichiers CSV nous avons intérêt à encapsuler le plus possible le parsing et à le rendre plus facilement reexploitable.

L'une des voies serait de créer une classe totalement dédiée à cette tâche. C'est une solution envisageable mais elle est assez lourde et amène son lots de difficultés.

Nous allons choisir ici une autre approche, celle de la création d'un class helper. C'est à dire que nous souhaitons non pas créer une classe qui traite un fichier CSV comme un tout, mais nous voulons pouvoir parser n'importe quelle chaîne de caractères formatée en CSV. L'approche est très différente. Dans le premier cas il nous faudra complexifier de plus en plus notre classe, voire créer une hiérarchie de classes pour traiter les fichiers CSV mais aussi les flux mémoire CSV, et puis encore les services Web retournant du CSV, etc, etc.

Dans le second cas nous allons plutôt ajouter la capacité à la classe string de parser une chaîne donnée. La source de la chaîne ne nous importe pas. Il pourra s'agir d'un élément d'un tableau de chaîne comme dans le premier exemple autant que d'une chaîne lue depuis un flux mémoire, une section data d'un fichier XML, etc. D'un certain sens nous acceptons d'être moins "complet" que l'option "classe dédiée CSV", mais nous gagnons en agilité et en "réutilisabilité" en faisant abstraction de la provenance de la chaîne à parser. Bien entendu nous pouvons nous offrir le luxe d'une telle approche car nous savons que nous pouvons nous reposer sur C# 3.0, ses class helpers et LINQ...

Le projet qui accompagne ce billet contient tout le code nécessaire et même un fichier CSV d'exemple, nous n'entrerons pas dans les détails de l'implémentation du class helper lui-même qui étend la classe string, parser du CSV n'est qu'un prétexte sans plus d'intérêt dans ce billet. Le code utilisé pour l'exemple provient d'ailleurs d'un billet de  Eric White dont vous pouvez visiter le blog si vous lisez l'anglais.

la façon d'écrire un class helper est décrite dans mon article sur C# 3.0, regardons juste sa déclaration :

public static string[] CsvSplit(this String source)

Cette méthode est déclarée au sein d'une classe statique de type "boîte à outils" pouvant centraliser d'autres méthodes utilitaires. La déclaration nous montre que le class helper s'applique à la classe String uniquement (this String source) et qu'elle retourne un tableau de chaîne (string[]).

Une fois le class helper défini (code complet dans le projet accompagnant l'article) il nous est possible d'écrire des requêtes LINQ du même type que celle du premier exemple. Mais cette fois-ci le parsing CSV est réalisé par le class helper ce qui permet à la fois de le rendre plus sophistiqué et surtout de ne plus avoir à le copier/coller dans les requêtes LINQ...

Voici un exemple d'utilisation du class helper sur le même fichier CSV. Ici nous parsons la source, nous la trions, nous éliminons les lignes de commentaire mais aussi nous créons en réponse une classe anonyme contenant le nom du contact, sa ville et le nom de sa société. Il est dès lors possible de traiter la liste d'objets résultat comme n'importe quelle liste : affichage, traitement, génération d'état, etc.

   1:  var data = File.ReadAllLines("Export Sales Info-demo.csv").Where(s=>!s.StartsWith("#"))
   2:      .Select(l =>
   3:  {
   4:  var split = l.CsvSplit();
   5:  return new
   6:             {
   7:                 Contact = split[0],
   8:                 City = split[1],
   9:                 Company = split[2]
  10:             };
  11:  }  ).OrderBy(x=>x.Contact);
  12:   
  13:  foreach (var d in data.Take(5))
  14:      Console.WriteLine(d);

Ce qui, avec le même fichier source, affichera à la console :

{ Contact = Allen James, City = San Jose, Company = City Center Lodge }
{ Contact = Bart H. Perryman, City = San Francisco, Company = Kwik-e-mart }
{ Contact = Beth Munin, City = Seattle, Company = Little Corner Sweets }
{ Contact = Beth Munin, City = Seattle, Company = Little Corner Sweets }
{ Contact = Bruce Calaway, City = Irvine, Company = Penny Tree Foods Corporation } 

Conclusion

La bonne utilisation de C# 3.0 permet de réduire significativement la taille du code donc de réduire dans les mêmes proportions les bugs et d'augmenter la productivité. Apprendre à utiliser cette nouvelle approche c'est gagner sur tous les plans...

 

Le projet exemple : LinqToCsv.zip (9,46 kb)

Utiliser des clés composées dans les dictionnaires

Les dictionnaires sont des listes spécialisées permettant de relier deux objets, le premier étant considéré comme la clé, le second comme la valeur. Une fois clé et valeur associées au sein d'une entrée du dictionnaire ce dernier est capable de retourner rapidement la valeur de toute clé. Les dictionnaires peuvent être utilisés en de nombreuses circonstances, comme la conception de caches de données par exemple.

Un dictionnaire se créée à partir de la classe générique Dictionnary<Key,Value>. Comme on le remarque si la clé peut être de tout type elle reste monolithique, pas de clés composées donc, et encore moins de classes telles Dictionnary<Key1,Key2,Value> ou Dictionnary<Key1,Key2,Key3,Value> etc...

Or, il est assez fréquent qu'une clé soit composée (multi-part key ou composed key). Comment utiliser les dictionnaires génériques dans un tel cas ?

La réponse est simple : ne confondons pas une seule clé et un seul objet objet clé ! En effet, si le dictionnaire n'accèpte qu'un seul objet pour la partie clé, rien n'interdit que cet objet soit aussi complexe qu'on le désire... Il peut donc s'agir d'instances d'une classe créée pour l'occasion, classe capable de maintenir une clé composée.

Vous allez me dire que ce n'est pas bien compliqué, et vous n'aurez qu'à moitié raison...

Créer une classe qui contient 2 propriétés n'est effectivement pas vraiment ardu. Prenons un exemple simple d'un dictionnaire associant des ressources à des utilisateurs. Imaginons que l'utilisateur soit repéré grâce à deux informations, son nom et une clé numérique (le hash d'un password par ex) et imaginons, pour simplifier, que la ressource associée soit une simple chaîne de caractères.

La classe qui jouera le rôle de clé du dictionnaire peut ainsi s'écrire en une poignée de lignes :

   1:  public class LaClé
   2:  {
   3:    public string Name { get; set; }
   4:    public int PassKey {get; set; }
   5:  }

Oui, c'est vraiment simple. Mais il y a un hic !

En effet, cette classe ne gère pas l'égalité, elle n'est pas "comparable". De base, écrite comme ci-dessus, elle ne peut pas servir de clé à un dictionnaire...

Pour être utilisable dans un tel contexte il faut ajouter du code afin de gérer la comparaison entre deux instances. Il existe plusieurs façons de faire, l'une de celle que je préfère est l'implémentation de l'interface générique IEquatable<T>. On pourrait par exemple choisir une autre voie en implémentant dans la classe clé une autre classe implémentant IEqualityComparer<T>. Toutefois dans un tel cas il faudrait préciser au dictionnaire lors de sa création qu'il lui faut utiliser ce comparateur là bien précis, cela est très pratique si on veut changer de comparateur à la volée, mais c'est un cas assez rare. En revanche si demain l'implémentation changeait dans notre logiciel et qu'une autre structure soit substituée au dictionnaire il y aurait de gros risque que l'application ne marche plus: les objets clés ne seraient toujours pas comparables deux à deux "automatiquement".

L'implémentation d'une classe utilisant IEqualityComparer<T> est ainsi une solution partielle en ce sens qu'elle réclame une action volontaire pour être active. De plus cette solution se limite aux cas où un comparateur de valeur peut être indiqué.

C'est pour cela que je vous conseille fortement d'implémenter directement dans la classe "clé" l'interface IEquatable<T>. Quelles que soient les utilisations de la classe dans votre application l'égalité fonctionnera toujours sans avoir à vous soucier de quoi que ce soit, et encore moins, et surtout, des éventuelles évolutions du code.

Comme par enchantement l'excellent Resharper (add-in pour VS totalement indispensable) sait générer automatiquement tout le code nécessaire, je n'ai donc pas eu grand chose à saisir pour le code final... Ceux qui ne disposent pas de cet outil merveilleux pourront bien entendu s'inspirer de l'implémentation proposée pour leur propre code.

Le code de notre classe "clé" se transforme ainsi en quelque chose d'un peu plus volumineux mais de totalement fonctionnel :

   1:  public class ComposedKey : IEquatable<ComposedKey>
   2:          {
   3:              private string name;
   4:              public string Name
   5:              {
   6:                  get { return name; }
   7:                  set { name = value; }
   8:              }
   9:   
  10:              private int passKey;
  11:              public int PassKey
  12:              {
  13:                  get { return passKey; }
  14:                  set { passKey = value; }
  15:              }
  16:   
  17:              public ComposedKey(string name, int passKey)
  18:              {
  19:                  this.name = name;
  20:                  this.passKey = passKey;
  21:              }
  22:   
  23:              public override string ToString()
  24:              {
  25:                  return name + " " + passKey;
  26:              }
  27:   
  28:              public bool Equals(ComposedKey obj)
  29:              {
  30:                  if (ReferenceEquals(null, obj)) return false;
  31:                  if (ReferenceEquals(this, obj)) return true;
  32:                  return Equals(obj.name, name) && obj.passKey == passKey;
  33:              }
  34:   
  35:              public override bool Equals(object obj)
  36:              {
  37:                  if (ReferenceEquals(null, obj)) return false;
  38:                  if (ReferenceEquals(this, obj)) return true;
  39:                  if (obj.GetType() != typeof (ComposedKey)) return false;
  40:                  return Equals((ComposedKey) obj);
  41:              }
  42:   
  43:              public override int GetHashCode()
  44:              {
  45:                  unchecked
  46:                  {
  47:                      return ((name != null ? name.GetHashCode() : 0)*397) ^ passKey;
  48:                  }
  49:              }
  50:   
  51:              public static bool operator ==(ComposedKey left, ComposedKey right)
  52:              {
  53:                  return Equals(left, right);
  54:              }
  55:   
  56:              public static bool operator !=(ComposedKey left, ComposedKey right)
  57:              {
  58:                  return !Equals(left, right);
  59:              }
  60:          }

Désormais il devient possible d'utiliser des instances de la classe ComposedKey comme clé d'un dictionnaire générique.

Dans un premier temps testons le comportement de l'égalité :

   1:  // Test of IEquatable in ComposedKey
   2:  var k1 = new ComposedKey("Olivier", 589);
   3:  var k2 = new ComposedKey("Bill", 9744);
   4:  var k3 = new ComposedKey("Olivier", 589);
   5:   
   6:  Console.WriteLine("{0} =? {1} : {2}",k1,k2,(k1==k2));
   7:  Console.WriteLine("{0} =? {1} : {2}",k1,k3,(k1==k3));
   8:  Console.WriteLine("{0} =? {1} : {2}",k2,k1,(k2==k1));
   9:  Console.WriteLine("{0} =? {1} : {2}",k2,k2,(k2==k2));
  10:  Console.WriteLine("{0} =? {1} : {2}",k2,k3,(k2==k3));

Ce code produira le résultat suivant à la console :

Olivier 589 =? Bill 9744 : False
Olivier 589 =? Olivier 589 : True
Bill 9744 =? Olivier 589 : False
Bill 9744 =? Bill 9744 : True
Bill 9744 =? Olivier 589 : False

Ces résultats sont conformes à notre attente. Nous pouvons dès lors utiliser la classe au sein d'un dictionnaire comme le montre le code suivant :

   1:  // Build a dictionnary using the composed key
   2:  var dict = new Dictionary<ComposedKey, string>()
   3:                 {
   4:                     {new ComposedKey("Olivier",145), "resource A"},
   5:                     {new ComposedKey("Yoda", 854), "resource B"},
   6:                     {new ComposedKey("Valérie", 9845), "resource C"},
   7:                     {new ComposedKey("Obiwan", 326), "resource D"},
   8:                 };
   9:   
  10:  // Find associated resources by key
  11:   
  12:  var fk1 = new ComposedKey("Yoda", 854);
  13:  var s = dict.ContainsKey(fk1) ? dict[fk1] : "No Resource Found";
  14:  // must return 'resource B'
  15:  Console.WriteLine("Key '{0}' is associated with resource '{1}'",fk1,s);
  16:   
  17:  var fk2 = new ComposedKey("Yoda", 999);
  18:  var s2 = dict.ContainsKey(fk2) ? dict[fk2] : "No Resource Found";
  19:  // must return 'No Resource Found'
  20:  Console.WriteLine("Key '{0}' is associated with resource '{1}'", fk2, s2);

Code qui produira la sortie suivante :

Key 'Yoda 854' is associated with resource 'resource B'
Key 'Yoda 999' is associated with resource 'No Resource Found'

Et voilà ...

Rien de tout cela est compliqué mais comme on peut le voir il y a toujours une distance de la coupe aux lèvres, et couvrir cette distance c'est justement tout le savoir-faire du développeur !

Le projet VS 2008 : MultiKey.zip (5,84 kb)

.. Stay Tuned !

Donnez des couleurs à Blend et Design (intégration de Adobe Kuler).

Comme une coïncidence je parlais de Adobe dans mon dernier billet... Et si je n'en faisais pas forcément l'éloge pour la facette programmation de leurs outils, en revanche je reconnaissais bien volontier leur talent en ce qui concerne les outils de graphisme. En voici une illustration, et un moyen de récupérer ce savoir-faire dans les produits de design de la gamme Microsoft Expression !

Adobe a mis en ligne un site qui s'appelle Adobe Kuler, son utilité est de permettre facilement, et visuellement, de créer des nuanciers de couleurs. Les graphistes utilisent fréquemment les nuanciers car une fois ajoutés à un projet ils sont le garant du respect de la charte décidée pour celui-ci.

Bien entendu les outils tels que Expression Design ou Expression Blend savent gérés les nuanciers, chacun à sa façon. Design le gère de façon similaire à Adobe Illustrator, Blend le fait sous la forme de ressources de brosses. Mais qu'importe la technique exacte cela revient au même. Surtout, les contraintes qu'un graphiste doit prendre en compte restent les mêmes, qu'il travaille sous Illustrator ou Design ou même Blend : le respect de la charte est essentiel.

Je ne pourrais pas dire, faute de le connaître, si Jonas Follesoe est type bien ou sympa, en revanche je peux vous affirmer que c'est un bon développeur WPF qui aime Blend et Design ! Et il a créé un add-in pour ces deux produits qui permet de disposer d'une palette supplémentaire se connectant à Adobe Kuler pour aller y chercher des nuanciers !

Recherche des favoris, des plus populaires, etc, voire recherche sur un mot (tous les nuanciers "ocean" par exemple), tout cela est possible. On récupère les nuanciers par simple copie dans le logiciel (ou même par drag'n drop). C'est vraiment génial. D'autant qu'on peut aussi accéder au site Adobe Kuler pour modifier un nuancier ou en créer un totalement nouveau. Le site propose des modes intelligents comme la création de palettes à partir d'algorithmes de type couleurs complémentaires, nuancier monochromatique, etc.

Toutes les instructions pour télécharger le plugin Blend/Design ainsi que l'application WPF stand-alone sur son le blog de Jonas.

Le site Adobe Kuler vaut lui aussi une visite si vous ne connaissez pas.

Enfin, le projet de Jonas est sur CodePlex, dont open source, toujours bon pour repiquer les bonnes idées de développement et progresser dans son apprentissage !

Et comme c'est joli toutes ces couleurs, voici une petite capture écran du plugin en action sur mon Blend 2 SP1 :

 

Silverlight 2.0 RC disponible (avec les MAJ de VS 2008 et Blend)

L'avènement de Silverlight approche... Si la version 1.0 a surtout été l'occasion "d'occuper le marché", la version 2.0 marquera très certainement la véritable entrée de cette technologie dans la réalité des développements Web.

Silverlight 1.x c'était du XAML et du Javascript, vous allez me dire ce n'est pas pire que Flash ou Flex avec leur ActionScript (que je n'ai jamais aimé). Mais cela n'était bien évidemment pas une finalité en soi. Faire aussi bien (ou mal) que Flash, c'est une ambition assez limitée, avouons-le.

Silverlight 2.0 c'est toute la couche graphique (avec des améliorations) de la V 1 plus la programmation en langage .NET (C# ou VB généralement) pour le "code behind" selon un principe identique à celui des pages ASP.NET par exemple.

Dit comme ça, cela ne semble pas si révolutionnaire... Et pourtant  !

Silverlight est un plugin pour Web browser, pas un ActiveX dédiée à IE. Le plugin existe pour Windows et IE mais aussi pour Linux et Mac OSX ! Du coup Silverlight 2.0 c'est "la" réponse au développement multi plateforme : la possibilité de développer des applications riches qui font tourner du .NET et qui fonctionnent sur un PC qu'il soit équipé de OSX (un Mac donc), de Windows XP/Vista ou de Linux !

Imaginez ce que vous pouvez faire avec un tel outil ? Tout en conservant votre savoir-faire .NET et C# ou VB.Net vous pouvez concevoir des applis Internet ou Intraweb pour des environnements hétérogènes. La révolution est bien là. Silverlight est la première technologie de ce type.

Certains dirons, un peu chagrins, que Adobe et son Flash ça existe depuis des lustres, pas de quoi en faire une tartine donc.

Si je comprends cette première impression, je leur demanderais de bien vouloir examiner la différence qui peut exister entre un Flash développé par une société spécialiste du graphisme mais sans aucun savoir-faire en terme d'environnement de développement ou d'OS, et un Silverlight qui lui repose sur le framework .NET avec des EDI comme VS 2008 ou Blend, sans aucun équivalent chez Adobe (ou autre d'ailleurs). La nuance est là et elle est de taille. Pour un développeur qui veut faire de vraies applications en tout cas, et pas seulement des bandeaux de pub...

Le mieux c'est de vous rendre compte par vous-mêmes hein...

La version 2.0 RC de Silverlight est désormais disponible, les téléchargements intègrent le plugin lui-même ainsi que le SDK et le Service Pack 1 de Blend 2 qui remplace Blend 2.5 .

Une petite chose qu'il faut savoir : la RC 2.0 est une preview mise à disposition en US comme toutes les previews ou presque. De fait le SP1 de Blend 2 ne fonctionne que sur la version US, pas sur la version française. Pour profiter d'un Blend 2 capable de travailler avec Silverlight 2 il est donc nécessaire de désinstaller Blend 2 français pour installer la version US. Il existe une version trial chez MS, si vous aviez un Blend 2 avec licence, normalement la version US trial se tansformera en version réelle. Dans tous les cas ce "montage" est temporaire : dans quelque temps nous passerons de la RC à la release finale et le SP1 de Blend 2 sera fourni en FR aussi, bien entendu (il sera alors possible de réinstaller votre Blend 2 FR pour lui appliquer la SP1 FR définitive).

Silverlight c'est beau, c'est cool, et c'est du .NET multi plateforme. N'hésitez pas plus longtemps !

Open XML SDK et PowerTools pour OOXML - Manipuler les documents OOXML

Le format Open XML utilisé par Microsoft pour sa suite Office est une grande avancée en ce sens que le stockage n'est plus propriétaire et que cela permet d'envisager de nombreux traitements automatiques des documents qu'il s'agisse de recherche documentaire, de modification ou même de création de nouveaux documents.

Le format en lui même est aujourd'hui normalisé ISO/IEC et même si on peut regretter la guerre des formats entre Open XML de Microsoft et OpenDocument, il va falloir apprendre à jongler avec ces deux normes ouvertes, aucune n'étant plus "sympathique" ou "meilleure", et chacune affichant ses petites différences. Quoi qu'il en soit, dans les faits la masse de documents produite par la suite Office dans le monde est tellement gigantesque qu'au final le développpeur aura forcément plus souvent affaire à de l'Open XML qu'à du OpenDocument.

Il faut donc saluer Microsoft d'avoir opté pour un nouveau format lisible, public et normalisé. Pour nous, informaticiens, c'est la seule chose qui compte. Laissons les querelles de format à ceux qui ont du temps à perdre...

Open XML SDK 

Même si OOXML est une norme claire, les documents la décrivant sont, comme toute norme, un peu imbuvables, avouons-le... Mais bien heureusement il existe un SDK gratuit "Open XML SDK" qui se compose d'aide et d'une librairie de classes .NET facilitant la manipulation des fichiers de type Word, Excel, PowerPoint...

Vous pouvez télécharger le OOXML SDK en cliquant sur le lien.

Une fois cet assemblage référencé, vos applications peuvent facilement traiter tout document Office.

A titre d'exemple vous trouverez un petit projet console qui permet de lister le contenu d'un fichier OOXML (Word, Excel, PowerPoint). J'ai supprimé du projet les fichiers de test, il vous suffira de modifier le code de la méthode Main pour y placer le chemin d'un fichier OOXML se trouvant sur votre machine (ou une copie si vous être méfiant ! ).

Voici un exemple de sortie sur un fichier Excel très simple :

URI                        Content Type
===                        ============
/xl/workbook.xml           application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml
/xl/worksheets/sheet.xml   application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml
 

Ce n'est qu'une sorte de "hello world", le SDK permettant de vraiment traiter les documents et de faire des choses bien plus sophistiquées, bien entendu !

PowerTools pour OOXML

Les PowerTools pour OOXML sont eux une autre couche s'utilisant d'ailleurs avec PowerShell dont je vous parlais dernièrement. C'est un projet open source sur CodePlex dont le but est d'autoriser le traitement de fichiers OOXML côté serveur. Par exemple produire directement un document Word ou Excel sur un serveur Web pour l'envoyer à l'utilisateur, faire des recherches dans une base documentaire, etc...

Lien : PowerTools OOXML sur CodePlex

On notera ce billet Julien Chable sur l'installation des PowerTools (en français, pour une fois...), ainsi qu'une vidéo de présentation (US) sur MSN Video

L'avantage de la combinaison des PowerTools OOXML au PowerShell et ses scripts est de former un ensemble permettant de traiter automatiquement des documents Offices en dehors de toute application. C'est donc une autre façon de traiter les documents Office que celle proposée par le SDK présenté plus haut.

Un exemple concret d'utilisation parmi des milliers d'autres : ajouter un filigrane avec le nom de la société ou une mention "ne pas diffuser" (ou autre) automatiquement à tous les documents d'un répertoire.  On peut même concevoir un service Windows surveillant un ou plusieurs répertoires et ajoutant systématiquement une mention, un logo, un filigrane, etc, à tout nouveau document qui est déposé. On peut envisager par le même biais d'extraire les propriétés de tout document déposé dans un répertoire et de l'envoyer par mail à un administrateur. Les idées ne manquent pas, à vous de jouer !

Conclusion

Disposer de tels outils ouvre la voie à l'intégration de fonction de GED de haut niveau dans toutes les applications, Web ou Desktop. C'est aussi un moyen simple de produire des documents OOXML loin des bricolages DDE, COM/DCOM, ActiveX ou autres pour faire de l'Automation. De plus les fichiers peuvent être manipulés sans que les applications équivalentes du pak Office ne soient installées, c'est un énorme avantage.

le projet (il faut installer le SDK OOXML of course) : PowerOPX.zip (2,91 kb)

Un éclairage sur les techniques d'accès aux données sous .NET

Depuis la sortie de .NET Microsoft n'arrête plus sa course folle ! La plupart des technologies publiées faisaient partie d'un plan presque totalement connu à l'avance, comme WPF, WCF etc. Il a fallu du temps pour qu'émerge ses "modules" de .NET car même le plus puissant des éditeurs de logiciels du monde ne peut pas releaser la totalité d'une montagne comme .NET en un seul morceau. S'il était clair que WPF serait la nouvelle gestion des interfaces utilisateurs et que Windows Forms n'était qu'un "os à ronger" en attendant, le foisonnement des technologies tournant autour des données n'était pas forcément visible ni prévisible il y a quelques années. Et même aujourd'hui savoir ce qui existe et comment s'en servir avec l'ensemble des autres technologies n'est pas forcément une mince affaire !

Un petit dessin valant tous les discours, voici un diagramme qui tourne sur les blogs américains de Microsoft et que j'ai en partie traduit pour vous, en espérant que cela vous aidera à y voir plus clair !

Quelques précisions pour certains acronymes ou noms de technologies :

[Faite un clic-droit sur l'image et copiez-la pour l'afficher en 100% dans Word ou autre ]

Powershell ou la ligne de commande objet sous .NET de Windows

Le shell de Windows nous renvoie aux temps préhistoriques où le PC avait un écran vert non graphique. Même les premières versions de Windows se lançaient aussi en ligne de commande. On ne parlait d'ailleurs pas de logiciels en "mode console" puisque c'était le mode normal... Le fameux DOS.

Avec les versions modernes de Windows est venu un autre temps, celui où Windows est devenu le DOS et où la ligne de commande n'est plus qu'une console qu'on appelle parfois et qui, une fois fermée, retourne à Windows, qu'on n'a pas quitté d'ailleurs... Inversion de tendance, mais le côté "rugueux", voire ésotérique de la console est resté.

Bien que le shell soit avec le temps devenu capable de faire des choses plus intelligentes il n'a finalement que peu évolué depuis MS-DOS.

Mais voici PowerShell !

PowerShell n'est pas un utilitaire freeware mais bien une extension Windows produite par Microsoft. Il s'agit de proposer une console (un shell) totalement objet fonctionnant sous .NET. Les commandes ne retournent plus des réponses textuelles mais des listes d'objets.

Certes, quand on tape un "dir" on obtient, grosso-modo, le même type d'affichage qu'avant. Mais cela est fort trompeur ! En réalité le "dir" (comme les autres commandes) retourne une liste d'objets dont on peut obtenir les méthodes, les propriétés. On peut aussi exécuter les méthodes de ces objets, remettre en forme la liste et bien d'autres choses encore !

Un exemple simple en quelques étapes :

  • obtenir la liste des services tournant sur la machine: get-service
  • obtenir cette liste et obtenir la classe des objets retournés ainsi que les membres de cette classe : get-service | get-member
  • obtenir le service de planification des tâches et savoir s'il peut être arrêté : (get-service schedule).CanStop
  • obtenir le service de planification des tâches et l'arrêter : (get-service schedule).stop()

etc...

Comme on le voit ici le "pipe" fonctionne toujours mais il sait passer les objets d'une commande à l'autre. On voit aussi qu'on peut obtenir un objet en particulier et invoquer ses méthodes ou accéder en lecture et en écriture à ses propriétés.

Le PowerShell c'est encore bien d'autres choses, des boucles ForEach par exemple, des Cmdlet (prononcer Command-let) des petites commandes toutes prêtes ou des alias permettant d'écrire moins de code. C'est aussi des scripts bien entendu ou l'accès à tous les drives de la machines même ceux plus abstraits comme Env (l'environnement) ou HKCU (la registry, clé du current user) dans laquelle on peut naviguer comme dans tout drive. Etc.

Le mieux c'est de voir par vous-même n'est-ce pas ? ... En téléchargeant cet outil sur la page du PowerShell Microsoft.

Bon shell

.. et Stay Tuned !