Dot.Blog

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

Deboguer avec le code source de .NET sous VS 2008 !

Comme vous le savez peut-être, Microsoft a décidé d'ouvrir le code source de .NET, au moins en partie, pour aider les développeurs à mieux comprendre la plateforme mais aussi pour permettre un débogage en profondeur des applications.

La chose se sait (et encore, beaucoup semblent l'ignorer) mais fort peu de développeurs utilisent cette possibilité. Certainement faute de savoir comment trouver ce fameux code source et comment configurer VS 2008 pour y accéder automatiquement.

Il existe pourtant un site pour la "Share source initiative" (l'initiative de partage de source), site sur lequel vous trouverez toutes les explications pour configurer VS 2008 et télécharger le code source de .NET. Le lien suivant pointe la page d'explication pour configurer VS.

Pouvoir lire le code des classes qu'on utilise tous les jours (comme string par exemple), pouvoir déboguer en descendant au niveau d'un WriteLine et passer sa souris sur une valeur pour la vérifier, tout cela confère un avantage certain et, au bout du compte, une meilleure maîtrise du framework. Ne vous privez pas de ce privilège de pouvoir regarder derrière le rideau de scène et d'avoir accès au backstage comme un VIP !

Bon Debug !

Nouveau tutorial : RDL et ReportViewer, une solution gratuite de génération d'état

RDL ? Cela ne vous dit rien ? SSRS ou BIDS non plus ? Et le composant gratuit de Microsoft ReportViewer ? Rien ? ... Alors il faut que vous lisiez absolument ce nouveau tutorial (PDF, 30 pages + exemples VS 2008) que je viens de mettre en ligne !

Il existe en effet une solution gratuite et plus que performante pour générer des états, elle se cache dans Visual Studio et un peu aussi dans les méandres des sites Microsofts pour télécharger "ReportViewer" qui fonctionne sous VS 2005 et VS 2008.

Cette solution est plus qu'honorable : sous-états, états avec noeuds dépliables, prévisualisation avec recherche de texte, fonctionnant sous Windows Forms et sous ASP.NET, utilisant toutes les sources de données possibles dont les grappes d'objets en mémoire, capable de produire des PDF, des fichiers Excel... Le tout gratuitement et assez facilement.

Rêve éveillé ? Non... mais en revanche, et pour des raisons que j'ignore, cette solution est méconnue. Elle mérite bien un tutor pour rétablir la justice (et vous faire économiser l'achat d'un générateur qui n'en fera peut-être pas la moitié) !

L'article RDL et ReportViewer est à télécharger en suivant le lien ou bien en se rendant sur www.e-naxos.com page Publications, rubrique Divers.

Bonne lecture ... et Stay Tuned !

[Vous pouvez aussi lire cet autre billet sur les best practices de RDL]

Astuce : reformater une chaîne sur plusieurs lignes (comme une requête SQL par exemple)

Il arrive souvent qu'on ait besoin de faire un copier-coller d'une chaine de caractères fractionnée en plusieurs lignes. Qu'il s'agisse d'un bout de texte avec des retours à la ligne ou bien par exemple une requête SQL, s'il y a beaucoup de lignes la mise en forme est fastidieuse. Il faut indenter chaque ligne, mettre des guillemets au début et à la fin, ajouter un "+" à chaque ligne... C'est vraiment le genre de chose qui coupe l'élan en plein milieu du codage d'une méthode.

Mais il existe une solution simple, très simple même, qui fait intervenir le chercher/remplacer de Visual Studio avec deux mini expressions régulières, tellement petites que même si vous êtes réfractaire aux ER je suis sûr que celles là vous les noterez et les utilisez !

Acte 1 : une belle requête

Supposons une requête ni trop petite ni trop grosse, pour notre exemple, par exemple générée par un outil comme MKQB ou un autre VQB :

SELECT
  dbo.INPUTUNIT.BATCH_ID,
  dbo.INPUTUNIT.FOLD_ID,
  dbo.INPUTUNIT.DOCUMENT_ID,
  dbo.INPUTUNIT.SHEET_ID,
  dbo.INPUTUNIT.PAGE_ID,
  dbo.INPUTUNIT.ENTITYID,
  dbo.INPUTUNIT.CREATEDON,
  dbo.INPUTUNIT.CREATEDBY,
  dbo.INPUTUNIT.MODIFIEDON,
  dbo.INPUTUNIT.MODIFIEDBY,
  dbo.INPUTUNIT.LASTERROR,
  dbo.INPUTUNIT.NAME,
  dbo.INPUTUNIT.TYPE,
  dbo.INPUTUNIT.IMAGEID,
  dbo.INPUTUNIT.STATE,
  dbo.INPUTUNIT.REASON,
  dbo.INPUTUNIT.EXPIRYDATE,
  dbo.INPUTUNIT.POSITION,
  dbo.INPUTUNIT.PRIORITY,
  dbo.INPUTUNIT.COMMINGFROMSTEP,
  dbo.INPUTUNIT.NEXTWORKFLOWSTEP,
  dbo.INPUTUNIT.CURRENTVALUE,
  dbo.INPUTUNIT.REGEXPRESSION,
  dbo.INPUTUNIT.NULLALLOWED,
  dbo.INPUTUNIT.CHECKMINMAX,
  dbo.INPUTUNIT.MINVALUE,
  dbo.INPUTUNIT.MAXVALUE,
  dbo.INPUTUNIT.VALUELIST,
  dbo.INPUTUNIT.MULTIVALUE,
  dbo.INPUTUNIT.STRICTVALUELIST,
  dbo.INPUTUNIT.CROPZONE,
  dbo.INPUTUNIT.INPUTCOUNT,
  dbo.INPUTUNIT.CONFIDENCEINDEX,
  dbo.INPUTUNIT.FORMAT,
  dbo.INPUTUNIT.DATATYPE,
  dbo.INPUTUNIT.DECIMALDIGITS,
  dbo.INPUTUNIT.MONEYFACTOR,
  dbo.INPUTUNIT.CASING,
  dbo.INPUTUNIT.CHECKLENGTH,
  dbo.INPUTUNIT.MAXLENGTH,
  dbo.INPUTUNIT.MINLENGTH,
  dbo.INPUTUNIT.MINSECURITYLEVEL,
  dbo.INPUTUNIT.CHECKDLL,
  dbo.INPUTUNIT.DLLCHECKNUMBER,
  dbo.INPUTUNIT.COMMENT
FROM
  dbo.INPUTUNIT
  INNER JOIN dbo.FOLD ON (dbo.INPUTUNIT.FOLD_ID = dbo.FOLD.ENTITYID)
  INNER JOIN dbo.BATCH ON (dbo.INPUTUNIT.BATCH_ID = dbo.BATCH.ENTITYID)
WHERE
  (dbo.FOLD.FOLDTYPE = 0) AND
  (dbo.BATCH.CUSTOMERID LIKE '%') AND
  (dbo.BATCH.BUSINESSID LIKE '%') AND
  ((dbo.INPUTUNIT.STATE = 5) OR
  (dbo.INPUTUNIT.STATE = 6))
 

C'est une requête somme toute très classique (et réelle, prise au hasard d'une appli en cours). Mais voilà, elle est sur "plein de lignes", elle est indentée avec des espaces au début, il va falloir jongler au clavier pour supprimer les espaces, placer les guillemets au début et à la fin et un petit signe + pour finir...
Nous commençons par coller cette requête dans le corps d'une méthode. je vous fait grace de la capture écran, on trouve une méthode "machin" ses accolades de début et de fin et ... notre belle requête en plein milieu comme un cheveu sur la soupe de C#.

Acte 2 : Désindenter

Pour ça, c'est facile, mais peut-être ne connaissez-vous l'astuce : sélectionnez tout le bloc texte en question, et tapez autant de fois que nécessaire la combinaison SHIFT-TAB jusqu'à ce que toutes les lignes se retrouvent collées à gauche. C'est un bon début et c'est rapide à faire.

Acte 3 : Chercher - Remplacer

Le plus gros de l'effort a été fait, et nous n'avons pas fait grand chose pour le moment...
Vérifiez que le texte est toujours sélectionné puis ouvrez la boîte de dialogue Chercher/Remplacer (CTRL-H) et renseignez les champs comme le montre cette capture écran :

 

Le texte à chercher est {.+} et le texte de remplacement est +"\1 ", cochez "Use " puis "Regular expression" (Utiliser/Expressions régulières pour la version FR). Puis cliquez sur "Replace All" (Remplacer tout). Vous noterez l'espace après le "1", c'est au choix, il ajoute un espace à chaque ligne avant de mettre le guillement fermant, pour une requête SQL c'est putôt préférable, dans d'autres circonstances cela peut gêner ou bien être remplacé par un autre symbole.

Et voici le résultat (du à la mise en page la requête est indentée sous la méthode, en réalité elle reste collée à gauche) :

static void Main(string[] args)
{
+
"SELECT "
+
"dbo.INPUTUNIT.BATCH_ID, "
+
"dbo.INPUTUNIT.FOLD_ID, "
+
"dbo.INPUTUNIT.DOCUMENT_ID, "
+
"dbo.INPUTUNIT.SHEET_ID, "
+
"dbo.INPUTUNIT.PAGE_ID, "
+
"dbo.INPUTUNIT.ENTITYID, "
+
"dbo.INPUTUNIT.CREATEDON, "
+
"dbo.INPUTUNIT.CREATEDBY, "
+
"dbo.INPUTUNIT.MODIFIEDON, "
+
"dbo.INPUTUNIT.MODIFIEDBY, "
+
"dbo.INPUTUNIT.LASTERROR, "
+
"dbo.INPUTUNIT.NAME, "
+
"dbo.INPUTUNIT.TYPE, "
+
"dbo.INPUTUNIT.IMAGEID, "
+
"dbo.INPUTUNIT.STATE, "
+
"dbo.INPUTUNIT.REASON, "
+
"dbo.INPUTUNIT.EXPIRYDATE, "
+
"dbo.INPUTUNIT.POSITION, "
+
"dbo.INPUTUNIT.PRIORITY, "
+
"dbo.INPUTUNIT.COMMINGFROMSTEP, "
+
"dbo.INPUTUNIT.NEXTWORKFLOWSTEP, "
+
"dbo.INPUTUNIT.CURRENTVALUE, "
+
"dbo.INPUTUNIT.REGEXPRESSION, "
+
"dbo.INPUTUNIT.NULLALLOWED, "
+
"dbo.INPUTUNIT.CHECKMINMAX, "
+
"dbo.INPUTUNIT.MINVALUE, "
+
"dbo.INPUTUNIT.MAXVALUE, "
+
"dbo.INPUTUNIT.VALUELIST, "
+
"dbo.INPUTUNIT.MULTIVALUE, "
+
"dbo.INPUTUNIT.STRICTVALUELIST, "
+
"dbo.INPUTUNIT.CROPZONE, "
+
"dbo.INPUTUNIT.INPUTCOUNT, "
+
"dbo.INPUTUNIT.CONFIDENCEINDEX, "
+
"dbo.INPUTUNIT.FORMAT, "
+
"dbo.INPUTUNIT.DATATYPE, "
+
"dbo.INPUTUNIT.DECIMALDIGITS, "
+
"dbo.INPUTUNIT.MONEYFACTOR, "
+
"dbo.INPUTUNIT.CASING, "
+
"dbo.INPUTUNIT.CHECKLENGTH, "
+
"dbo.INPUTUNIT.MAXLENGTH, "
+
"dbo.INPUTUNIT.MINLENGTH, "
+
"dbo.INPUTUNIT.MINSECURITYLEVEL, "
+
"dbo.INPUTUNIT.CHECKDLL, "
+
"dbo.INPUTUNIT.DLLCHECKNUMBER, "
+
"dbo.INPUTUNIT.COMMENT "
+
"FROM "
+
"dbo.INPUTUNIT "
+
"INNER JOIN dbo.FOLD ON (dbo.INPUTUNIT.FOLD_ID = dbo.FOLD.ENTITYID) "
+
"INNER JOIN dbo.BATCH ON (dbo.INPUTUNIT.BATCH_ID = dbo.BATCH.ENTITYID) "
+
"WHERE "
+
"(dbo.FOLD.FOLDTYPE = 0) AND "
+
"(dbo.BATCH.CUSTOMERID LIKE '%') AND "
+
"(dbo.BATCH.BUSINESSID LIKE '%') AND "
+
"((dbo.INPUTUNIT.STATE = 5) OR "
+
"(dbo.INPUTUNIT.STATE = 6)) "
}

Il n'y a plus qu'à se placer au début de la chaîne et de taper "var machaine = " puis de se placer en fin de la dernière ligne de la chaîne, de taper ";" et hop! c'est tout fini tout beau, remis en page et sans risque d'oubli ou de fausse manip ! Là aussi je vous fait grace de la capture écran, vous imaginez facilement le résultat, et puis, profitez-en pour tout de suite refaire la manip histoire de la mémoriser !

Conclusion

Une astuce simple qui rend un grand service et une preuve de plus que pour rébarbatives et contre-intuitives que soient les expressions régulières, savoir s'en servir est toujours un gros avantage...

Bon dev.. et Stay Tuned !

 

Un cadeau à télécharger : des exemples LINQ !

Dernièrement je vous parlais des "101 exemples LINQ" publiés par Microsoft et je vous donnais les liens vers la page Web de ces derniers ainsi que vers un téléchargement d'une application complète distribuée avec la Ctp de mars 2007.

Hélas, qu'il s'agisse du site Web ou bien de l'appli en question, le code n'est pas tout à fait fonctionnel. On peut toujours le lire pour regarder les exemples de syntaxe LINQ mais il est quand même plus agréable de disposer d'une application qui marche pour tester.

A l'époque de la sortie des "101 exemples" j'avais commencé à me bricoler une application. En partant des exemples publiés sur le site Web j'avais construit une appli de test "laboratoire" pour expérimenter des tas de choses pas forcément liées à LINQ. Quand j'ai étudié plus tard l'exemple MS de la Ctp de mars 2007 je me suis aperçu que face aux mêmes problèmes le concepteur de cette dernière et moi avions finalement trouvé des solutions très proches. Et comme la version MS gérait deux ou trois choses que je n'avais pas implémentées j'ai décidé, en raison des ressemblances des codes, de les fusionner en une application simple et fonctionnelle.

Ce week-end le temps n'étant pas vraiment à la baignade (l'eau est bonne mais le vent de nord... qui interdit d'aller à la pêche aussi...) j'ai ressorti ce projet que j'avais laissé dans un coin de disque. je l'ai dépoussiéré et finalisé pour être utilisable. Il a profité de ce nettoyage d'hiver (ah bon ? c'est l'été ?) puisque cela a permis de le reprendre sous VS 2008 SP1 alors que ma version originale tournait sous la bêta. Au passage cela m'a permis de vérifier la bonne tenue de route de Resharper 4.0 qui gère vraiment bien les nouveautés de C# 3.0 et de LINQ (je vous en parlais aussi dernièrement).

Le code est téléchargeable ici (code source + l'exécutable dans bin/debug) : Linq101.rar (488,53 kb)

Une petite image de l'interface pour la route :

Bon téléchargement,

et ... Stay Tuned !

L'outil le plus intelligent enfin à jour ! (Resharper 4.0)

Resharper, si vous ne connaissez pas c'est que vous ne développez pas sous .NET ! C'est en effet un add'in incroyablement intelligent et indispensable une fois qu'on l'a essayé.

Il apporte à Visual Studio (depuis VS 2003) les petits plus qui transforment cet excellent IDE en une bombe proche de la perfection. Personnellement je ne peux plus m'en passer depuis longtemps.

Il y avait toutefois un bémol avec VS 2008, même si la version 3.1 était bien intégrée à l'IDE, elle ne savait pas gérer les dernières nouveautés du langage comme LINQ par exemple (oui encore LINQ...) ce qui gâchait le plaisir et obligeait à déconnecter bon nombre des features de Resharper lorsqu'on abordait un code plein de requêtes LINQ, d'expressions Lambda et autres derniers bijous syntaxiques.

Mais enfin tout est rentré dans l'ordre !

Avec un peu de retard sur la sortie de C# 3.0 (masi quel boulot le concepteur a du fournir !) la version 4.0 de Resharper est enfin disponible. Elle ajoute plein de nouvelles possibilités à cet outil déjà très complet mais, Ô comble de la satisfaction enfin retrouvée, elle gère parfaitement C# 3.0 et toutes ses nouvelles tournures, dont LINQ bien entendu.

A 199 tous petits dollars tous dévalués, pour la licence un développeur, c'est presque un cadeau et ça serait surtout une mesquinerie énorme que de ne pas se l'offrir (l'upgrade coûte encore moins cher si vous possédez une 3.0 et même une 2.0 pour VS 2003 me semble-t-il).

Pour ceux qui ne connaissent pas je ne vais pas détailler tout ce que Resharper fait déjà ni même les nouveautés de la 4.0, c'est trop long. Le mieux c'est que vous alliez directement sur le site de JetBrains et que vous regardiez de plus près les démos (fixes ou captures d'écrans animées flash).

N'oubliez pas aussi que Resharper supporte des plugins comme AgentSmith qui offre des contrôles supplémentaires comme par exemple la correction orthographique ou le contrôle des conventions de nommage (AgentSmith est gratuit, ce qui ne gâche rien).

Bon dev ...

... Et Stay Tuned !

Lé fotes d'ortografe ds les programs é lé page web c mauche !

Les fôtes d'ortografes dans les blogs, c courant, avec les sms les jeunes ne sav + écrir c connu.

Mouais... mais dans les applications c'est la tehon, disons-le franchement !

Et qui, ici, perd son temps à faire du copier coller de toutes les chaînes de caractères de son code pour les tester sous Word et les corriger ? Hein ? qui ? .... j'en étais sûr.

Je plaide coupable aussi d'ailleurs. Et c'est vraiment pas une bonne chose, car comme tout le monde, même en relisant j'en laisse passer des fautes.. On ne peut se concentrer sur le code qui implique logique et rigueur d'esprit et en même temps sur l'orthographe et la grammaire de notre langue, totalement illogique et sans rigueur d'esprit, je suis bien d'accord, je ne vous le fait pas dire. Mais quand même...

L'orthographe est sans nul doute la science des ânes, c'est la sagesse populaire qui le dit, mais les fautes dans les écrans des applications, qu'il s'agisse d'applis de bureau ou pire de pages Internet visibles par la planète bizarrement, et paradoxalement, ça ne vous fait pas passer non plus pour le plus brillant des intellectuels. C'est injuste je sais, mais c'est comme ça.

Il faut donc trouver une solution, simple et efficace qui ne réclame surtout pas trop de boulot à mettre en oeuvre, l'informaticien est fainéant, c'est connu (en tout cas les meilleurs, c'est presque un critère de sélection :-) ).

Si cette solution est gratuite c'est encore mieux (l'informaticien aime ce qui est gratuit, c'est aussi connu). Alors jetez un oeil sur IntelliSpell, un outil de Component One qui existe en version "community" gratuite. C'est ici, en cliquant ce lien. Un bémol tout de même, la version gratuite semble ne fonctionner qu'avec le dictionnaire US, quelle que soit la locale de votre Windows ou de VS 2008. J'ai demandé des précisions à Component One sur le sujet, mais visiblement ils ne sont pas pressés de répondre aux questions. Dommage. Mais l'outil reste intéressant à tester malgré tout.

Il existe bien entendu d'autres "spell checkers" pour Visual Studio, nommons sans ordre de préférence, à vous de voir :

  • StudioSpell
  • Arkhipov  [edit: en novembre 2012 le site ne propose plus ce lien, sorry]
  • Agent Smith (plugin gratuit pour Resharper)
  • et bien d'autres...

Si vous en connaissez un particulièrement bien, n'hésitez pas à l'indiquer en laissant un commentaire !

NB: ceux qui trouveront des fautes dans ce post auront gagné... le droit de se taire, on va pas jouer à ce jeu là hein, ça serait terriblement mesquin...Wink

Stay Tuned !

Linq au quotidien, petites astuces et utilisations détournées...

Linq je vous en parle souvent, c'est tellement génial. Et ce que j'aime bien c'est trouver des utilisations presque à contre-sens de Linq.
J'entends par là des façons de "rentabiliser" Linq à toute autre tâche que d'interroger des données (SQL, XML ou autres).

Je ne suis pas le seul dans ce cas ! J'ai au moins trouvé un autre zigoto qui s'amuse au même jeu, Igor Ostrovsky dont vous pouvez visiter le blog (cliquez sur son nom). Comme il s'exprime en anglais et que je sais que certains visiteurs ici ne sont pas forcément doués pour cette langue, voici une adaptation libre(*) de son billet présentant quelques astuces très amusantes.
(*) je n'ai repris que les exemples de code, le texte et le projet exemple sont de mon cru, si vous parlez anglais je vous laisse découvrir la prose de Igor sur son blog.

Initialiser un tableau, une liste

Il est souvent nécessaire d'initialiser une array avec des éléments qui, pour que cela soit utile, répondent à certains critères. Normalement cela se règle soit sous la forme d'une liste de constantes dans la déclaration de l'array, ce qui est pour le moins assez peu souple, soit sous la forme d'un code d'initialisation, ce qui du coup devient assez lourd pour parfois pas grand chose... C'est oublier que Linq peut apporter ici une aide non négligeable !

Voici trois exemples :

int[] A = Enumerable.Repeat(-1, 10).ToArray();
int[] B = Enumerable.Range(0, 10).ToArray();
int[] C = Enumerable.Range(0, 10).Select(i => 100 + 10 * i).ToArray();

Les trois arrays contiendront 10 éléments. "A" contiendra 10 éléments tous égaux à -1. "B" contiendra la suite 0 à 9, et "C" contiendra la suite de 100 à 190 par pas de 10.

Amusant, n'est-il pas ?

Plusieurs itérations en une seule

Imaginons que nous possédions deux arrays, A1 et B2, chacune contenant une sélection différente d'éléments. Supposons que souhaitions appliquer le traitement "Calcule(x)" aux éléments de ces deux arrays. La solution s'imposant serait :

foreach (var x in A1) { Calcule(x); }
foreach (var x in A2) { Calcule(x); }

Ce n'est pas très long, mais répéter du code n'est jamais très élégant. Voici comme Linq permet de faire la même chose en une seule boucle :

foreach (var x in A1.Concat(A2)) { Calcule(x); }

L'opérateur Concat effectue une .. concaténation, donnant l'illusion à notre boucle qu'elle travaille sur une seule et même array alors qu'il n'en est rien... On notera que Linq travaillant au niveau des énumérateurs, il n'y même pas de variable temporaire créée par le mécanisme pour stocker la concaténation. C'est élégant et en plus c'est efficace du point de vue de la mémoire.

Fun, isn't it ?

Créer des séquences aléatoires

Il est parfois nécessaire de créer des séquences aléatoires, pour tester un bout de code par exemple. La classe Random est bien pratique mais encore une fois il faudra déclarer le tableau puis déclarer une méthode pour l'initialiser en utilisant Random. Avec Linq tout cela peut se réduire à deux déclarations :

Random rand = new Random();
var ListeAléatoire = Enumerable.Repeat(0, 10).Select(x => rand.Next(10,500));

Le tableau "ListeAléatoire" contiendra 10 nombres aléatoires entre 10 et 500, et pas une seule déclaration de méthode à l'horizon. Vous noterez en revanche l'utilisation qui est faite de l'expression Lambda (nouveauté de C# 3.0) au niveau du Select.

Créer des chaînes de caractères

Si les exemples portent jusqu'ici sur des tableaux de nombres, c'est par pure souci de simplification. Il est bien entendu possible d'appliquer les mêmes astuces à d'autres types de données comme les chaînes :

 string chaine = new string( Enumerable.Range(0, 20) .Select(i => (char)(‘A’ + i % 3)) .ToArray());

La chaîne "chaine" contiendra "ABCABCABCABCABCABCAB". Remplacez le "20" par un "rand.Next(10,100)" par exemple, et en plus la longueur de la chaîne sera elle-même aléatoire. C'est pas pratique ça ?

Dans les commentaires du blog de Igor, un lecteur proposait une autre façon d'initialiser une chaîne avec Linq, la voici pour l'intérêt de la démonstration de la versatilité de Linq :

string chaine2 = string.Join(string.Empty, Enumerable.Repeat("ABC", 7).ToArray());

Ici on peut passer une pattern et un nombre de répétition.

Convertir une valeur en séquence de 1 objet

Il est parfois nécessaire de convertir une valeur en une séquence de longueur 1. Passer un paramètre unique là où un tableau de valeur est attendu réclame de créer le fameux tableau et de l'initialiser. Rien de bien méchant mais pourquoi ne pas mettre Linq à contribution ?

IEnumerable<int> sequence = Enumerable.Repeat(LaValeur, 1);

La répétition de 1 élément peut sembler être une sorte de contresens, mais si les concepteurs de la fonction partageaient cette opinion n'auraient-ils pas levé une exception pour toute valeur inférieure à 2 ... :-)
C'est en tout cas un moyen bien pratique d'arriver au résultat souhaité en une seule opération !

Enumérer tous les sous-ensembles d'un tableau

Il s'agit plus ici d'un "plaisir pervers" que d'une réelle utilité quotidienne il faut l'avouer, mais pour l'intérêt de la syntaxe voici comment énumer tous les sous ensembles d'un tableau (attention, si la taille du tableau dépasse quelques dizaines d'éléments le temps de calcul peut vite exploser ...) :

T[] tableau = …;
var subsets = from m in Enumerable.Range(0, 1 << tableau.Length)
              select
                  from
i in Enumerable.Range(0, tableau.Length)
                  where (m & (1 << i)) != 0
                  select arr[i];

Je vous laisse réfléchir à la syntaxe, peu évidente de prime abord, et le côté un peu "tordu" de cette astuce, mais elle répond à un besoin qui l'est tout autant. Et Linq peut aussi servir à cela.
Quelques clés pour comprendre : la requête ne retourne pas des items de "tableau" mais bien des sous ensembles de "tableau", c'est à dire des tableaux. Chaque tableau contient une combinaison unique d'éléments de "tableau". Si vous prenez tableau = B de notre premier exemple (c'est à dire la suite d'entiers 0 à 9), la requete Linq ci-dessus retournera 1024 sous-ensembles. Avouez que bien qu'un peu difficile à comprendre à première lecture, le procédé est d'une grande élégance... (Vous pouvez regarder le fonctionnement de cette requête de plus près grâce au projet démo téléchargeable en fin d'article).

Un peu de code...

Pour mieux comprendre les choses il est toujours préférable de faire tourner un bout de code. J'y ai pensé ! Vous trouverez un projet console complet plus bas ;-)

Conclusion

Igor montre au travers de ces différentes utilisations de Linq ce que je tente aussi de vous montrer dans mes billets : Linq est d'une grande souplesse, Linq ne se limite pas à faire du SQL en C#, Linq est un atout majeur pour le développeur qui sait s'en servir au quotidien. Initialiser un tableau, convertir un tableau dans un type différent, jouer sur les combinatoires, fabriquer des chaînes de caractères ou des séquences aléatoires de test, etc, etc. Il n'y a pas un bout de code dans lequel il ne soit possible de glisser un peu de Linq pour le rendre plus sobre, plus élégant et souvent plus performant.
Remercions Igor pour ces exemples de code (seule chose reprise de son billet, j'ai mon propre style, il a le sien :-) )

Le code complet

LinqFun.rar (99,18 kb)

Bonus 

Mitsu, qu'on ne présente plus, ça serait faire insulte à sa notoriété !, a proposé il y a quelques temps déjà une autre astuce Linq tout à fait amusante, elle consiste à obtenir la décomposition d'un nombre en facteurs premiers au travers de requêtes Linq et d'un class helper permettant d'écrire : 144.ToPrimeNumbersProduct() et d'obtenir en sortie "2^3 * 3^2 * 7". Je vous en conseille la lecture en suivant ce lien.

 

Le blues du générateur d'état (sous titré: Et si RDL était la solution ?)

Il n'y a pas que le "Set of" de Delphi qui peut donner le blues (voir ce billet pour comprendre), non, il y a pire, sous tous les langages, depuis toujours, sous tous les OS, l'informaticien a toujours eu le blues des générateurs d'état, et le blues, c'est peu dire...

[EDIT: Voir ce billet plus récent qui propose un tutor complet] 

Les delphistes ont pesté contre QuickReport et ses bugs après avoir ronchonné sur ReportSmith (qui s'en souvient?), Borland l'a remplacé un jour par RaveReport, encore pire, Microsoft intègre de longue date Crystal Report qui n'a jamais eu les faveurs des développeurs (comment peut-il encore exister d'ailleurs si personne ne l'aime ? un mystère), sans trop rien dire tout en le disant sans le dire, Microsoft propose un service de génération d'état dans Sql Server. Quelle drôle d'idée que de le lier au serveur (même s'il y a une logique compréhensible) plutôt que d'en faire un produit à part réellement intégré à VS. Mais peu de gens s'en servent et MS ne semblent pas non plus le promouvoir avec force et vigueur...

Je suis convaincu que les Java-istes, les C-istes, et même les cobolistes (heeuu non, eux ils faisaient les états à la main avec des petites étoiles pour dessiner les cadres), enfin à part les cobolistes donc, je suis convaincu que tous les développeurs du monde ont toujours été déçus par les générateurs d'état.

La faute à qui ? A la nature même des états... Un état cela peut être tout et n'importe quoi. D'un simple "listing" à l'ancienne, jusqu'à la mise en page d'une ordonnance ou d'un rapport d'activité annuel, d'une facture à une liasse fiscale, d'un chèque à un mailing publicitaire... Une vraie liste à la Prévert ! Et chacun de ces documents a ses propres exigences, ses priorités, ses "non négociables". Un mailing avec enveloppe à fenêtre ou une liaisse fiscale pré-imprimée devront se reposer sur un outil très précis au niveau du placements des zones, pour un listing c'est la facilité et la rapidité de mise en page en colonnes, les sous-totaux, les regroupements, les ruptures qui seront essentiels..

Bref, le générateur d'état idéal et parfait n'existe pas. Il en existe de très mauvais (non, je te citerai pas de noms !), d'autres meilleurs, mais aucun n'est adapté à tous les besoins.

RDL vous connaissez ?

Report Definition Language. Une norme de description d'état dérivée de XML, voilà ce qu'est RDL. C'est sur ce langage d'ailleurs que repose le service générateur d'état de SQL Server 2005 et 2008, mais RDL existe en dehors de cette base de données. Et c'est tout là son intérêt !

La description de la norme de novembre 2005 se trouve ici, une lecture édifiante, mais peu passionnante il faut l'avouer.

Le site suivant (ici) regroupe des informations complémentaires sur RDL ainsi que le composant ReportViewer de Microsoft qui s'intègre à Visual Studio (une version VS 2005 et une pour VS 2008 existent).

On trouve même ici un moteur RDL autonome ainsi qu'un designer visuel évitant de trop mettre les mains dans XML. Gratuit et avec les sources, accompagné d'un serveur d'états, il s'agit là d'une base intéressante.

RDL et ses trops rares outils restent encore trop confidentiels, la puissance descriptive du langage est pourtant plus qu'intéressante, la possibilité de requêter des sources SQL mais aussi des services Web, des flux RSS, etc, n'est qu'une des facettes de cette puissante alternative aux générateurs d'état classiques.

RDL est-t-il le générateur d'état idéal ?
Difficile à dire car c'est avant tout un langage, mais justement, là est sa force : les états deviennent descriptifs, "générables" par programmation (fabriquer un fichier XML à coup de WriteLine dans un fichier texte est d'une simplicité enfantine), partageables (RDL est un format ouvert et publié), et les moteurs traitant RDL savent exporter les états en PDF, HTML, CSV, MHT, Texte, RTF, etc..

Il n'y a pas à l'heure actuelle (à ma connaissance) de superbes designers hyper sexy pour RDL (mais VS 2008 intègre un designer visuel, le saviez-vous ?!), l'information est difficile à trouver, c'est le côté "underground" de RDL.. Mais si comme tous les informaticiens de la planète (et peut-être même de quelques exoplanètes!) vous avez le blues du générateur d'état, alors jetez un oeil à RDL, il y a des chances que cela vous séduise. Dans tous les cas vous aurez au moins gagné un sujet de conversation hyper branché pour la machine à café !

Merci qui ? ... Laughing

Alors stay tuned !

Les class Helpers, enfer ou paradis ?

Class Helpers 

Les class helpers sont une nouvelle feature du langage C# 3.0 (voir mon billet et mon article sur les nouveautés de C# 3.0).

Pour résumer il s'agit de "décorer" une classe existante avec de nouvelles méthodes sans modifier le code de cette classe, les méthodes en questions étant implémentées dans une autre classe.

Le principe lui-même n'est pas récent puisque Borland l'avait "inventé" pour Delphi 8.Net (la première version de Delphi sous .Net) afin de permettre l'ajout des méthodes de TObject à System.Object (qu'ils ne pouvaient pas modifier bien entendu) afin que la VCL puisse être facilement portée sous .Net. Borland avait déposé un brevet pour ce procédé (ils le prétendaient à l'époque en tout cas) et je m'étonne toujours que Microsoft ait pu implémenter exactement la même chose dans C# sans que cela ne fasse de vagues. Un mystère donc, mais là n'est pas la question.

Mauvaises raisons ?

Les deux seules implémentations de cet artifice syntaxique que je connaisse (je ne connais pas tout hélas, si vous en connaissez d'autres n'hésitez pas à le dire en commentaire que l'on puisse comparer) sont donc celle de Borland dans Delphi 8 pour simplifier le portage de la VCL sous .NET et celle de Microsoft dans C# pour faciliter l'intégration de Linq dans le langage.

Deux exemples, deux fois pour la même raison un peu spécieuse à mes yeux : simplifier le boulot du concepteur du langage pour supporter de nouvelles features. Deux fois une mauvaise raison à mes yeux, un peu trop puristes peut-être, qui pensent qu'un élément syntaxique doit se justifier d'une façon plus forte, plus théorique que simplement "pratique".

Résultat, je me suis toujours méfié des class helpers car leur danger est qu'un objet se trouve d'un seul coup affublé de méthodes "sorties d'un chapeau", c'est à dire qu'il semble exposer des méthodes publiques qui ne sont nulles part dans son code. J'ai horreur de ce genre de combines qui, à mon sens, favorise un code immaintenable. Si j'adore la magie, à voir ou à pratiquer, je la déteste lorsque je porte ma casquette d'informaticien... J'ai donc toujours conseillé la plus grande circonspection vis à vis de cet artifice syntaxique, que ce soit à l'époque (déjà lointaine.. le temp passe!) où je faisais du Delphi que maintenant sous C# qui vient d'ajouter cette fioriture à sa palette.

Jusqu'à aujourd'hui, faute d'avoir trouver une utilisation intelligente des class helpers qui ne puissent être mise en oeuvre plus "proprement", les class helpers étaient à mon sens plutôt à classer du côté enfer que paradis. Interface et héritage m'ont toujours semblé une solution préférable à ces méthodes fantomes.

Trouver une justification

Mais il n'y a que les imbéciles qui ne changent pas d'avis, n'est-ce pas... Et je cherche malgré tout toujours à trouver une utilité à un outil même si je le trouve inutile de prime abord, réflexe d'ingénieur qui aime trouver une place à toute chose certainement.
J'ai ainsi essayé plusieurs fois dans des projets de voir si les class helpers pouvaient rendre de vrais services avec une réelle justification, c'est à dire sans être un cache misère ni une façon paresseuse de modifier une classe sans la modifier tout en la modifiant...

Comme j'ai enfin trouvé quelques cas (rares certes) dans lesquels les class helpers me semblent avoir une justification pratique, je me suis dit que vous en toucher deux mots pourraient éventuellement faire avancer votre propre réflexion sur le sujet (même si j'ai bien conscience que je dois être assez seul à me torturer la cervelle pour trouver absolument une utilité aux class helpers :-) ).

Bref, passons à la pratique.

Un cas pratique

Premier cas, les chaînes de caractères. Voilà un type de données vieux comme la programmation qui pourrait être un bon candidat à l'exploitation des possibilités des class helpers. En effet, hors de question de dériver la classe System.String et encore moins de pouvoir modifier le mot clé "string" de C#. Pourtant nous utilisons très souvent les mêmes fonctions "personnelles" sur les chaînes de caractères d'un même projet.

Par exemple, j'ai pour principe que les chaînes exposées par une classe (propriétés de type string donc) ne soient jamais à null. De ce fait, dans toutes les classes qui exposent un string, j'ai, dans le setter, la même séquence qui change l'éventuel null de value à string.Empty. C'est assez casse-pieds à répéter comme ça mécaniquement dans toutes les propriétés string de toutes les classes.

Et là, pas de possibilité de faire supporter une interface à System.String, ni de la dériver comme je le disais plus haut. C'est ici que les class helpers peuvent trouver une première justification pratique pour le développeur en dehors d'avoir facilité la vie à MS pour implémenter Linq.

Prenons le code suivant que nous plaçons dans une unité "Tools" de notre projet :

public static class Utilities
{

     public static string NotNullString(this string s)
      { 
return !string.IsNullOrEmpty(s) ? s.Trim() : string.Empty; }
...
 }

La classe Utilities est une classe statique qui contient tous les petits bouts de code utilisables dans tout le projet. Parmi ces méthodes, on voit ici l'implémentation du class helper "NotNullString". D'après cette dernière, la méthode ne sera visible que sur les instances de la classe "string". Le code lui-même est d'une grande simplicité puisqu'il teste si la chaîne en question est vide ou non, et, selon le cas, retourne string.Empty ou bien un Trim() de la chaîne. J'aime bien faire aussi systématiquement un Trim() sur toutes les propriétés string, je trouve ça plus propre surtout si on doit tester des équalités et que les chaînes proviennent de saisies utilisateurs.

Dans la pratique il suffira maintenant n'importe où dans notre projet d'écrire la chose suivante pour être certain qu'à la sortie de l'affectation la chaîne résultante ne sera jamais nulle et qu'elle ne comportera jamais d'espaces en trop en début ou en fin :

public string BusinessID
{  
get { return _BusinessID; }
    
set if (value != _BusinessID)
            
{ _BusinessID = value.NotNullString().ToUpperInvariant();
                
DoChange("BusinessID");
            
}
         
}
}

On voit ici une propriété string "BusinessID" qui, dans son setter, utilise désormais la nouvelle méthode fantome de la classe string... En fin de séquence nous sommes certains que _BusinessID est soit vide, soit contient un chaîne sans espace de tête ou de queue (et qu'elle est en majuscules, en plus dans cet exemple).

Voici donc une première utilisation "intelligente" des class helpers, la décoration d'une classe du framework (ou d'une lib dont on n'a pas le code source) pour lui ajouter un comportement, éventuellement complexe, dont on a souvent l'utilité dans un projet donné.

On pourrait penser ainsi à une fonction "ProperCase" qui passe automatiquement la casse d'une chaîne en mode "nom de famille", c'est à dire première lettre de chaque mot en majuscule, le reste en minuscule, ou à bien d'autres traitements qu'on aurait besoin d'appliquer souvent à des chaînes dans un projet.

Encore un autre cas

Dans la même veine, une application doit souvent manipuler des données issues d'une base de données et les modifier (en plus d'en insérer de nouvelles). On le sait moins (mais on s'aperçoit vite quand cela bug!) que le framework .NET possède, pour les dates par exemple, ses propres mini et maxi qui ne sont pas compatibles à toutes les bases de données, notamment SQL Server. Si vous attribuer à une date la valeur DateTime.MinValue et que vous essayez d'insérer cette dernière dans un champ Date d'une base SQL Server vous obtiendrez une exception de la part du serveur : la date passée n'est pas dans la fourchette acceptée par le serveur.
Dommage... DateTime.MinValue est bien pratique...

On peut bien entendu fixer une constante dans son projet et l'utiliser en place et lieu de DateTime.MinValue. Voci un exemple pour MaxValue (le problème étant le même):

DateTime MaxiDate = new DateTime(3000, 1, 1, 0, 0, 0);

Il suffira donc d'utiliser MaxiDate à la place de DateTime.MaxValue. La date considérée comme "maxi" est ici arbitraire (comme on le ferait pour la date mini) et est choisie pour représenter une valeur acceptable à la fois pour l'application et pour la base de données. Ici on notera que je prépare le terrain pour le bug de l'an 3000. Moins stupide qu'un coboliste et le bug de l'an 2000, vous remarquerez que je me suis arrangé ne plus être joignable à la date du bug et que mes héritiers sont aussi à l'abris de poursuites judiciaires pour quelques siècles :-)

L'utilisation d'une constante n'a rien de "sale" ou de "moche", c'est un procédé classique en programmation, même la plus éthérée et la plus sophistiquée. Toutefois, Puisque DateTime existe, puisque DateTime est un type complexe (une classe) et non pas un simple emplacement mémoire (comme les types de base en Pascal par exemple), puisque cette classe expose déjà des méthodes, dont Min et MaxValue, il serait finalement plus "linéaire" et plus cohérent d'ajouter notre propre MaxValue à cette dernière en place et lieu d'une constante.

Encore un bon exemple d'utilisation des class helpers. Ici nous homogénéisons notre style de programmation en évitant le mélange entre méthodes de DateTime et utilisation d'une constante. De plus, en ajoutant une méthode spécifique à DateTime, celle-ci sera visible par Intellisense come membre de DateTime, ce qui ne serait pas le cas de la constante. 

Dans la même classe Utilities nous trouverons ainsi :

public static DateTime SQLMaxValue(this DateTime d)
{
return new DateTime(3000, 1, 1, 0, 0, 0); }

 Et nous voici avec la possibilité d'écrire : MaDate = DateTime.SQLMaxValue();

Conclusion

Enfer ou paradis ? Les class helpers, comme tout artifice syntaxique peuvent se ranger dans les deux catégories, ils ne sont qu'un outil à la disposition du développeur. Un simple marteau peut servir à bâtir une maison où vivre heureux ou bien à assassiner sauvagement son voisin...  Les objets inanimés n'ont pas de conscience, ou plutôt, si, ils en ont une : celle de celui qui les utilise. A chacun d'utiliser les outils que la technologie humaine met à sa disposition pour créer un enfer ou un paradis...

Techniquement, je n'enfoncerai pas les portes ouvertes en donnant l'impression d'avoir découvert une utilisation miraculeuse des class helpers. Cela serait stupide, puisque justement cette syntaxe a été créée par Borland puis MS pour justement décorer des classes dont on ne possède pas le source (pas d'ajout de méthode ni d'interface) et qui ne sont pas héritables. En ajoutant des class helpers à string ou DateTime nous ne faisons rien d'autre que d'utiliser les class helpers exactement en conformité avec ce pour quoi ils ont été créés.

L'intérêt de ce billet se situe alors dans deux objectifs : vous permettre de réfléchir à cette nouveauté de C#3.0 que vous ne connaissez peut-être pas ou mal et vous montrer comment, en pratique, cela peut rendre des services non négligeables.

Si l'un ou l'autre de ces objectifs a été atteint, vous tenez alors votre récompense d'avoir tout lu jusqu'ici, et moi d'avoir écrit ce billet :-)

Stay tuned !

[EDIT: voir cet autre billet plus récent sur les class helpers]

ADO.Net Entity Framework - une série d'articles en US

Stefan Cruysberghs a publié (en anglais) une série de trois articles (récents) sur l'Entity Framework qui proposent un tour d'horizon assez intéressant de cette technologie.
En voici le sommaire (avec accès direct aux articles): 

Part 2 :

Part 3 :

Si vous préférez accéder aux trois articles séparément :

Bonne lecture !