Dot.Blog

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

C'est l'histoire de quatre personnes....

Comme il faut bien se détendre, mais intelligemment, voici l'histoire de quatre personnes un peu spéciales.
L'histoire originale est en anglais et "fonctionne" bien mieux dans cette langue puisque les quatre personnes en question s'appellent Everybody (Tout le monde), Somebody (Quelqu'un), Anybody (N'importe qui, surnomé Quiconque) et Nobody (Personne).

Si vous lisez l'anglais, voici l'original, pour les autre je fais suivre une tentative de traduction en français :

This is the story of four people named Everybody, Somebody, Anybody, and Nobody.
There was an important job to be done and Everybody was asked to do it.
Anybody could have done it, but Nobody did it.
Somebody got angry about that, because it was Everybody's job.
Everybody thought Anybody could do it, but Nobody realized that Everybody wouldn't do it.
Consequently, it wound up that Nobody told Anybody, so Everybody blamed Somebody.

C'est l'histoire de quatre personnes "ToutLeMonde", "Quelqu'un", "N'importe qui" et "Personne".
Il y avait un travail important à faire et "ToutLeMonde" a été invité à le faire. 
"Nimporte qui" aurait pu le faire, mais "Personne" l'a fait.
"Quelqu'un" s'est fâché à ce propos parceque c'était le boulot de "ToutLeMonde".
"ToutLeMonde" pensait que "N'importe qui" aurait pu le faire, mais "Personne" ne réalisa que "ToutLeMonde" ne le ferait pas.
Par conséquent, il s'avéra que "Personne" n'en parla à "Quiconque", et que "ToutLeMonde" blâma "Quelqu'un".

Bon, en anglais c'est bien mieux, la langue se prête mieux à ce genre de tournures qui m'ont obligé en français à tricher avec les négations et à donner un surnom à "Anybody" pour que ça colle mieux, ce qui en fait du mauvais français alors que l'original est en bon anglais...

Si "Quelqu'un" a quelque chose à redire sur ma traduction, pas la peine d'en parler à "ToutLeMonde". "Quiconque" le ferait serait nul comme "Personne" ! :-)

Bon bain de soleil et Stay Tunded !

Plongez dans l'infini d'une image... Deep Zoom arrive !

Deep Zoom... Si le nom ne vous dit rien pour le moment dans quelques lignes de là vous voudrez vous aussi jouer avec et surtout faire plein de jolis sites Web...

Deep Zoom est une technologie intégrée à Silverlight 2.0 qui permet de zoomer dans une image de taille gigantesque qu'il serait impossible d'envoyer au browser en un seul morceau. L'impossibilité est lié à deux facteurs principaux : une image de 20.000 x 20.000 même en 16 bits par exemple ça peut être trop gros pour la RAM de l'utilisateur, et puis surtout il faudrait avoir des visiteurs très très très patients le temps que toute l'image arrive ! Autre point, si l'image est une composition d'images, toutes les images de cette dernière n'intéressent pas forcément le visiteur, autant qu'il choisisse lui-même sur lesquelles zoomer et donc lesquelles doivent être transmises dans une résolution plus haute au browser.

Bon, tout cela c'est de la phrasologie et dans le domaine du visuel rien ne vaut mieux que de ... voir pour comprendre.

Je vous ai fait une petite collection de sites Web qui démontrent Deep Zoom, du site de la nouvelle Laguna à un site de soutien à Obama, on trouve un peu de tout, et justement on comprend tout de suite mieux le potentiel énorme de cette technologie.

Deep Zoom est téléchargeable sur les sites de Microsoft, tout comme Blend 2.5 (preview de juin) ou Silverlight 2.0 et toutes ces nouvelles choses. Je vous laisse utiliser Live Search (ou Google si vous préférez) pour trouver les adresses de tout ça (pour info le soft de composition s'appelle Deep Zoom Composer).

Nota: pour voir les sites qui suivent en action, vous devrez bien entendu installer Silverlight 2.0, mais il s'agit maintenant de la version finale ce n'est donc pas une mauvaise idée que de l'avoir sur sa machine car de plus en plus de sites vont utiliser ce plug-in...

... Et un petit dernier qui n'utilise pas Deep Zoom mais juste Silverlight 2.0 avec une simulation du (vieux) jeu Asteroid. Pour les nostalgiques qui ont connu les vieilles consoles de jeu Amiga dans les salles d'arcades (l'époque de Lunar Lander en 1979, juste après Space Invaders en 1978.... ahhhh) !

 L'été est l'occasion rêvée pour se former à toutes ses belles technologies, n'hésitez pas !

 .. Et Stay Tuned !

Un livre à avoir : "Framework Design Guidelines"

J'achète assez peu de livres en général, en effet dès qu'on s'intéresse à ce qui est nouveau tous les livres potentiellement intéressants sont "à paraître", et quand ils paraissent l'info est déjà réchauffée par rapport à ce qu'on peut trouver des mois avant sur Internet. Bien entendu cela implique d'aller chercher l'information brute, éparpillée et en anglais. C'est pour cela que les ouvrages techniques en français trouvent toute leur utilité en offrant une information structurée, vérifiée et traduite pour tous ceux qui ne lisent pas l'anglais.

Toutefois il m'arrive d'acheter certains livres lorsqu'ils traitent de sujet plus "intemporels" comme la méthodologie ou ici le framework .NET.

Oui, ce livre est en anglais, et à votre prochaine question la réponse est non, je n'en connais pas de traduction en français ce qui est de toute façon rare dans ce domaine. L'exemple récent de la fermeture de l'antenne française de O'Reilly illustre la difficulté globale de vendre du livre technique en France malgré un catalogue attrayant. Quant à traduire un ouvrage et donc réengager des frais importants pour un public très restreint, fort peu s'y sont risqués ou s'y risquent. Dans mes propres "development guidlines", en position 1 je mettrais "faites du C#", en 2 "faites du LINQ", mais en position 0 "faites de l'anglais" !.

Framework Design Guidelines

Ce livre traite des bonnes méthodes à mettre en oeuvre pour développer des applications respectueuses du framework .NET. Ecrit par deux membres de l'équipe de développement de .NET il a aussi été relu et corrigé par d'autres personnes de cette équipe qui, chose originale, font apparaître ici et là des petits encadrés où ils apportent leur point de vue sur ce que les auteurs viennent d'écrire. C'est une approche intéressante qui rend le contenu plus vivant. L'avant propos est d'ailleurs signé par Anders Hejlsberg, difficle de trouver mieux...

Ce n'est pas un livre très récent, il a du paraître en 2006 la première fois, mais son contenu est "intemporel" dans le sens où il dépasse les modes ou les dernières options du framework pour parler de son coeur, ses API, sa structure et la façon de bien coder dans cet environnement.

Le contenu

Le sous titre est finalement clair "Conventions, Idioms and Patterns for Reusable .NET Libraries". Conventions, idiomes et patterns pour des bibliothèque de codes réutilisables.

La première partie traite des "qualités d'un framework bien conçu" et on entre un peu dans les coulisses de .NET, comment et pourquoi certains choix ont été arretés. Une façon agréable, au delà du côté anecdotique, d'aborder les qualités générales que se doit posséder toute bonne bibliothèque de code, comme celles que nous sommes amenées parfois à écrire.

Les fondamentaux du framework sont ensuite abordés puis on trouve les guidelines proprement dites. Stratégie de nommage, le choix entre classe ou structure, entre classe abstraite ou interface, la conception des membres d'une classe (propriétés, méthodes, champs...) tout cela est traité avec intelligence, à propos, et permet d'affiner ses propres vues sur ces questions ou parfois même de s'interroger sur des sujets dont on pensait avoir fait le tour.

Les exceptions sont traitées en détail, toujours sous l'angle de la meilleure mise en oeuvre possible plus que sur le code ou la syntaxe. Ce livre est un moyen terme entre théorie, design pattern, expérience de développeurs ayant écrit l'un des plus gros framework existant, sagesse et doutes que tout informaticien conscient de l'importante de ses choix se doit d'avoir.

Un final sur l'utilisation de FxCop, son évolution, comment il marche et comment en tirer le meilleur est tout à fait appréciable. Je parlais justement dans un dernier billet de la "sous utilisation" de cet outil fanstastique par la majorité des développeurs que je rencontre ou que je forme. Ce chapitre permettra peut-être aux lecteurs de ce livre de faire plus souvent usage de FxCop.

Le CD

Le livre est accompagné d'un CD contenant des exemples de code et plusieurs vidéo de conférences (en anglais aussi, ne rêvez pas :-) ), toutes particulièrement intéressantes et portant sur des sujets aussi variés que le garbage collector (et tout ce qui tourne autour de la libération de la mémoire et des ressources), les performances ou le packaging et le déploiement. Une vraie mine d'information, pas forcément récentes, comme le livre lui-même, mais qui abordent des fondamentaux parfois négligés.

Conclusion

Comme je le disais, ce livre est intemporel, en tout cas tant que vivra le framework .NET sous la forme que nous connaissons depuis plusieurs années maintenant. Il permet d'entrer un peu dans l'équipe de développement de cette plateforme, de comprendre les choix qui ont présidé à sa mise en oeuvre et surtout il permet, par cette vision "de l'intérieur" et les nombreux conseils qu'il contient de mieux concevoir son propre code, de mieux trancher entre certaines options parfois trop nébuleuses (classe ou interface, classe ou structure, par exemple) en sachant avec précision pourquoi on fera ou non tel ou tel choix.

C'est un livre plaisant à lire, qu'on peut "consommer" en piochant les chapitres, pas forcément dans l'ordre du livre. Un "must have" pour qui développe sous .NET, c'est une évidence.

Références

Framework Design Guidelines

Krzysztof Cwalina (au scrabble si vous le placez vous mettez à genou vos adversaires !)
Brad abrams

0-321-24675-6

Edité par Addison-Wesley

Une bonne librarie technique en ligne

Vous pouvez acheter ce livre dans toutes les bonnes librairies, toutefois si vous voulez le payer le moins cher possible, je vous conseille de le commander directement à "The Book Depository Ltd", une librairies anglaise qui livre vite et bien qui affiche des prix en général très bas. Leur site Internet : http://www.bookdepository.co.uk/WEBSITE/WWW/WEBPAGES/homepage.php

C'est un vendeur qu'utilise Amazon, mais si vous passez en direct c'est généralement moins cher...

Bonne lecture et Stay Tuned !

Appel d'un membre virtuel dans le constructeur ou "quand C# devient vicieux". A lire absolument...

En maintenant un code C# d'un client mon ami Resharper me dit d'un appel à une méthode dans le constructeur d'une classe "virtual member call in constructor". J'ai tellement pris le pli avec ce problème que je ne m'en souci plus guère dans mon propre code, j'évite soigneusement la situation... Mais vous ? Avez-vous conscience de la gravité de ce problème ?

Sans Resharper il faut passer volontairement une analyse du code pour voir apparaître le message CA2214 "xxx contient une chaîne d'appel aboutissant à un appel vers une méthode virtuelle définie par la classe.". D'une part je doute fort que tout le monde comprenne du premier coup ce message ésotérique mais le pire c'est que je sais par expérience que la grande majorité des développeurs n'utilisent, hélas, que très rarement cette fonction... Et à la compilation du projet, aucune erreur, aucun avertissement ne sont indiqués !

Vous allez me dire "ça ne doit pas être bien grave si le compilateur ne dit rien et que seul un FxCop relève un simple avertissement". Je m'attendais à ce que vous me disiez cela... Et je vais vous prouver dans quelques lignes que cette remarque candide est la porte ouverte à de gros ennuis...

Le grave problème des appels aux méthodes virtuelles dans les constructeurs

Ce problème est "grave" à plus d'un titre. Tout d'abord techniquement, comme le code qui suit va vous le montrer, votre programme aura un comportement que vous n'avez pas prévu et qui mène à des bogues sournois. Cela est en soi suffisant pour qualifier le problème de "grave".
Ensuite, moins on a conscience d'un problème potentiel et plus il est grave, par nature. Comme très peu de développeurs ont conscience du fait que ce comportement bien particulier de C# est une source potentielle d'énormes problèmes, sa gravité augmente d'autant.
Pour terminer et agraver la situation, le compilateur ne dit rien et seule une analyse du code (ou l'utilisation d'un outil comme Resharper qui l'indique visuellement dans l'éditeur de code) peut permettre de prendre connaissance du problème.
La chaîne ne s'arrête pas là (tout ce qui peut aller mal ira encore pire - Murphy ), puisque même en passant l'analyseur de code le message sera noyé dans des dizaines, voire centaines d'avertissements et que, cerise sur le gateau, même si on prend la peine de lire l'avertissement, son intitulé est totalement nébuleux !

La preuve par le code

Maintenant que je vous ai bien alarmé, je vais enfoncé le clou par quelques lignes de code (qu'il est méchant Laughing) !

class Program
{
     static void Main(string[] args)
     {
        var derivé = new Derived();
     }
}

public class Base
{
   public Base()
   { Init(); }

   public virtual void Init()
  { Console.WriteLine("Base.Init"); }
}

public class Derived : Base
{
   private string s = "Non initialisée!";
   public Derived()
  { s = "variable initialisée"; }

  public override void Init()
 { Console.WriteLine("Derived.Init. var s = "+s); }
}

La question à deux eurocents est la suivante : Au lancement de la classe Program et de son Main, qu'est-ce qui va s'afficher à la console ?

La réponse est "Derived.Init. var s = Non initiliasée!".

L'action au ralenti avec panoramique 3D façon Matrix : Dans Main nous instancions la classe Derived. Cette classe est une spécialisation de la classe Base. Dans cette dernière il y a un constructeur qui appelle la méthode Init. Cette méthode est virtuelle et elle est surchargée dans la classe Derived.
Lorsque nous instancions Derived, de façon automatique le constructeur de Base se déclenche, ce qui provoque l'appel à Init. Donc à la version surchargée de Derived puisque C# appelle toujours la méthode dérivée la plus proche du type en cours.

D'où vient le problème ? ... Il vient du fait que le constructeur de Base, d'où provient l'appel à Init, n'est pas terminé (il le sera au retour de Init et une fois sa parenthèse de fin atteinte), du coup le constructeur de Derived n'a pas encore été appelé !

Si le code de Init ne repose sur aucune initialisation effectuée dans le constructeur de cette classe, tout va bien. Vous remarquerez d'ailleurs que le message affiché prend en compte la valeur de la variable s qui est initialisée dans sa déclaration et non pas une chaîne nulle. Ce qui prouve que les déclarations de variables initialisées sont, elles, bien exécutées, et avant le constructeur. Mais si le code de Init dépend de certaines initialisations effectuées dans le constructeur (initialisations simples comme dans l'exemple ci-dessus ou indirectes avec des appels de méthodes), alors là c'est la catastrophe : le constructeur de Derived n'a pas encore été appelé alors même que la version surchargée de Init dans Derived est exécutée par le constructeur de la classe mère !

La règle

Elle est simple : ne jamais appeler de méthodes virtuelles dans le constructeur d'une classe !

La règle CA2214 de l'analyseur de code :

"When a virtual method is called, the actual type that executes the method is not selected until run time. When a constructor calls a virtual method, it is possible that the constructor for the instance that invokes the method has not executed. "

"Quand une méthode virtuelle est appelée, le type actuel qui exécute la méthode n'est pas sélectionné jusqu'au runtime [ndt: c'est le principe des méthodes virtuelles, le "late binding"]. Quand un constructeur appelle une méthode virtuelle, il est possible que le constructeur de l'instance qui est invoquée n'ait pas encore été exécuté".

C'est "possible", c'est même pas sûr, donc il ne faut surtout pas écrire de code qui repose sur ce mécanisme...

L'aide de l'analyseur de code m'amuse beaucoup car dans sa section "How to fix violations" ("comment résoudre le problème"), il est dit tout simplement de ne jamais appeler de méthodes virtuelles dans les constructeurs... Avec ça débrouillez-vous !

La solution

Comme le dit laconiquement l'aide de l'analyseur : "faut pas le faire". Voilà la solution... En gros, si le cas se produit, comme dans notre exemple, la seule solution viable consiste à prendre le code de la méthode Init et à le déplacer dans le constructeur, il est fait pour ça... La méthode Init n'existe plus bien entendu, et elle est n'est donc plus surchargée dans la classe fille.

Conclusion

J'espère que ce petit billet vous aura aidé à prendre conscience d'un problème généralement méconnu, une spécificité de C# qu'on ne retrouve ni sous C++ ni sous Delphi.

Le projet VS2008 pour les fénéants : VirtualInit.rar (5,41 kb)

Interopératibilité Windows / Linux via MONO (suite...)

Pour ceux qui ont suivi mon billet précédent concernant l'installation d'une Mandriva et d'une OpenSuse sous VirtualBox dans un XP et de tout le bataclan MONO vous savez ce que je bricole ses jours-ci, pour les autres, et bien maintenant vous le savez :-) Le but du jeu n'est bien évidemment pas la prouesse de virtualiser un Linux sur XP, même si le chemin est long, mais de tester l'interopérabilité des exécutables .NET entre Windows et Linux.

Le casse-tête des installations et le pont réseau

Je ne vous résume pas l'épisode précédent, mais ce fut un peu laborieux. Mon dernier problème consistait à pouvoir lire mes disques XP depuis le Linux virtualisé. Ca n'a pas été facile mais j'y suis arrivé. Via un driver réseau qu'il faut installer à part de la VirtualBox Sun et qui monte dans Windows une connexion réseau spéciale (à laquelle on donne le nom qu'on veut). Ensuite il faut paramétrer VBox pour que l'instance virtualisée de Linux voit ce réseau interne et non pas la carte réseau (émulée) qui donne accès au web notamment. Mais cela n'est pas tout, il ne faut pas oublier d'aller dans les connexions de Windows, d'ouvrir les propriétés du Pont Réseau et de cocher les cases des réseaux qu'on veut relier dont le fameux réseau virtual créé pour la VBox! A partir de là on peut voir les répertoires partagés Windows sous OpenSuse et les ouvrir, sans perdre l'accès Web bien entendu. Magique...

Le casse-tête des Windows forms

Pour la petite histoire, faire du Gtk ne m'interpelle pas pour l'instant. Je veux pouvoir écrire une soft .NET avec Windows Forms et l'exécuter sous Linux. Donc j'ai besoin des Windows Forms sous MONO/Linux. Or, si l'assemblage des WF est bien présent dans les dernières livraisons de MONO, MonoDevelop, l'IDE, lui ne sait pas designer des forms WF... Diable!

Je vous passe les étapes, les recherches, les essais infructueux, j'en suis arrivé à installer MONO sous Windows et SharpDevelop, puisque celui-ci est sensé pouvoir cibler MONO. Oui... il était sensé faut-il préciser. SharpDevelop ne supporte plus MONO, ils se concentrent sur .NET tout court. Zut! Re-recherches, re-essais infructueux. Mais il existe dans les sources de SharpDevelop un répertoire Sample qui contient un sous-répertoire MONO. Qu'est-ce donc ? Un plug-in pour faire reconnaitre MONO à SharpDevelop (sous Windows donc). Bingo! Et ça marche.

La portabilité mise à l'épreuve

Une fois toutes ces usines à gaz configurées et capables de tourner sans problème (ce qui reste toujours un miracle à mes yeux), il restait à faire un premier test de portabilité. Ne soyons pas trop gourmands, disons juste un simple Hello Word en mode console. Faut y aller doucement !

Nouveau projet sous SharpDevelop (sous XP donc), cible MONO, projet console. On fait un Console.Writeline et une pause clavier histoire de voir si marche ou pas. Sous Windows le projet s'exécute sous MONO sans problème ("MONO montest.exe"), plus fort le même exécutable passe aussi directement ("montest.exe") et il est alors exécuté par le framework .NET Microsoft. Pas mal.

On lance OpenSuse dans VirtualBox, on accède via le réseau virtuel au répertoire partagé qui contient "montest.exe" sur XP et on exécute via MONO (comme sous Windows "mono montest.exe").. Et là, la magie s'opère, ce bel EXE créé sous Windows s'exécute sous OpenSuse sans aucune recompilation. C'est beau j'en pleurerais...

Conclusion

La prochaine étape: tester une application avec une form Windows Forms. Mais déjà à ce stade on peut affirmer que la compatibilité "binaire" des "exe" .NET entre Windows, Mono, et Linux est assurée, ce n'est pas de la science fiction, ça marche vraiment. je ne dis pas que des grosses applications peuvent passer sans recompilation, n'exagérons rien. Mais déjà les applis consoles, les libs de code, etc, tout ça passe.

.NET offre désormais ce que tout le monde à promis depuis des dizaines d'années sans vraiment y arriver : l'interopérabilité. Delphi avec Kylix avait fait une belle tentative, hélas sans succès (vendre un IDE Kylix à 20.000 francs de l'époque pour des linuxiens habitués à la gratuité, c'était crétin, tout autant que d'impose QT sous Windows), Java est le seul langage qui avait réussi à faire de l'interopérabilité, mais au prix de la lenteur légendaire d'un langage interprété et au prix d'une complexité et d'une gigantesque incompatibilités entre les VM et les libs diverses. Même si certains de ces défauts sont aujourd'hui gommés, .NET est un framework bien plus cohérent que Java, bien plus moderne, et sa normalisation, qui a permis le projet MONO, démontre toute la viabilité de cette plateforme même au-delà du cercle (déjà très large) de la communauté Windows.

Une fois tous mes tests terminés j'en ferai certainement un article, voir quelques chapitres d'un prochain bouquin à venir..
J'espère en tout cas que ces petits billets vous donneront, à vous aussi, l'envie de tester tout ça. .NET ouvre des portes, des tas de portes, c'est une gourmandise que de toutes les pousser et de découvrir derrière chacune d'elle un monde à s'approprier. Soyez gourmands aussi !

et... Stay Tuned !

C# MONO et OpenSuse, le tout virtualisé. Ou "comment occuper un geek" en une leçon.

Il faut bien se détendre un peu... Et que fait un geek pour se détendre ? Il allume son PC (en fait il ne l'éteint jamais !). Alors pour me changer les idées, je me suis mis en tête d'installer MONO pour "voir" où le projet en est. C'est cette petite aventure que je m'en vais vous conter, ça pourra vous servir...

Mais voilà, avant de jouer avec C# sous Linux il faut d'abord installer Linux (hmmm), choisir l'une de ses distribs et surtout arriver à le faire marcher ! Et comme je suis pervers mais pas fou, hors de question de faire un dual boot (j'en ai déjà un XP/Vista) ni même de reformater l'une de mes machines. Il va donc falloir virtualiser...

Abonné MSDN je dispose de tout plein de softs de Microsoft, dont Virtual PC 2007. J'ai commencé petit joueur par une Mandriva. Impossible à installer, le fameux problème d'écran 24 bits par défaut de Linux... Une vraie cata. J'ai passé des heures à chercher sur le Web, on trouve des trucs, mais aucun ne marche !

Après des heures à tourner en rond face à un écran très élargi et illisible, j'ai renoncé. J'ai donc laissé Virtual PC de côté, super chouette pour virtualiser du DOS, de l'XP ou du Vista, mais visiblement pas très open à Linux...

En farfouinant je suis tombé sur un virtualiseur gratuit écrit par l'ennemi juré de Microsoft, Sun. Du coup j'ai pas essayé de voir si leur machin savait lui gérer la virtualisation de Windows aussi mal que Virtual PC gère mal Linux, mais pour virtualiser du Linux ça semblait un bon choix. J'ai donc télécharger VirtualBox. Ca s'installe facilement, ça fait ce que ça dit et ça marche plutôt vite. Un bon point pour ce produit gratuit.

J'entreprends alors de virtualiser mon Mandriva que je n'avais pu installer sous Virtual PC. Et là, surprise, ça passe facile. Mon coeur s'emplit de joie en voyant Linux tourner dans une fenêtre... Mandriva, hélas, c'est KDE. L'avantage de KDE c'est d'être un clône de Windows XP niveau interface. On s'y retrouve facilement. Mais voilà, je n'y avais pas pensé avant, le kit de développement de MONO réclame visiblement GNOME ! Zut! me suis-je exclamé après toutes ses heures (en réalité c'était pas "zut", mais un truc du même genre en plus .. illustré :-) ). [EDIT:] Il semble que le site de MONO ignore Mandriva, mais depuis le gestionnaire de logiciels de ce dernier on peut accéder au téléchargement de MONO et des outils de développement. Je viens de le faire et oui, ça marche parfaitement. On peut donc utiliser Mandriva sans problème [/EDIT]. [EDIT2:] Comme je suis une quiche sous Linux, je n'avais pas pigé non plus que Mandriva permet de changer de bureau et de système d'affichage. On peut donc être en KDE ou GNOME sans problème la non plus. C'est en bûchant qu'on devient bucheron n'est-il pas... [/EDIT2]

Je farfouine à nouveau... et au final je me décide pour OpenSuse, promu par Novell qui se trouve derrière MONO aussi, ça devrait matcher. Bonne pioche ! Je prend la 11.0 mais ils préviennent que les fichiers de plus 4 Go posent des problèmes en téléchargement HTTP depuis Internet Explorer. J'avais téléchargé des DVD ISO il y a peu de temps avec IE sans problème et je me doutais bien qu'il s'agissait là de médisances de concurrents, mais par précaution j'ai pris l'option téléchargement par Torrent. Deux ou trois heures après j'avais mon ISO.

...Et on est reparti pour la création d'une machine virtuelle. Au passage je me dis, tiens, pourquoi ne pas redonner sa chance à Virtual PC, après tout. Je vous passe les détails, une heure de perdue pour exactement le même problème sans arriver à trouver une solution. Retour à VirtualBox.

Là les choses se passent comme avec Mandriva, c'est à dire bien et facilement.

Installation de OpenSuse. C'est pas mal. J'arrive ensuite à faire marcher l'installeur YaST pour ajouter les références au projet Mono et à installer l'ensemble des paquets.

Cool. J'ai maintenant un joli OpenSuse avec MONO et l'environnement de développement ! Je fais un nouveau projet console et je tape deux ou trois truc pour voir. Pas mal. C'est du niveau C# 2.0 avec les génériques. Je n'ai pas encore creusé plus loin, mais voir mon petit programme s'animer dans une fenêtre Linux sur mon XP, ça fait plaisir...

J'ai voulu aller un peu plus loin en partageant un disque physique de ma machine avec la machine virtuelle. VirtualBox le permet en installant des extensions. Retour à la galère, il faut les sources du noyau, le make et GCC. Trouver ces merveilles, les placer dans YaST et les installer s'est révélé moins dur que je le pensais. Mais la fameuse extension de VirtualBox n'a jamais voulu s'installer... C'est un script (.run) et quand je double cliquait dessus il me disait que j'avais pas les privilège admin ce qui plantait le script visiblement. Pourtant le compte que je me suis créé est dans le group des admins... Mystère de Linux. En changeant de user et en me loggant comme "root" c'est allé un cran plus loin. Mais là c'est un autre problème un peu confus que je n'ai pas pu résoudre qui s'est pointé... VirtualBox et ses extensions ne sont donc pas trop au point si on veut utiliser toutes les astuces. [EDIT] Devant l'impossibilité de partager un disque XP avec la VM j'ai essayé de faire un partage réseau... Mandriva ou OpenSus voient le réseau et Internet, mais malgré tous mes efforts à ce jour, impossible de leur faire voir les autres machines du réseau et encore moins leurs disques, malgré le daemon Lisa, malgré Samba et autres pares-feux aux configurations ésotériques... Si quelqu'un sait... laissez un commentaire![/EDIT]

Conclusion

Les leçons à tirer sont les suivantes : Il faut utilise VirtualBox et non Virtual PC si on veut virtualiser du Linux, Il faut prendre une distrib Linux compatible avec Mono, OpenSuse semble parfaite pour ça [EDIT] Mandriva va très bien aussi [/EDIT]. Sinon pour le reste ça s'installe correctement, YaST simplifiant les choses (mais il faut comprendre comment marche YaST, notamment l'ajout d'une source d'installation, c'est là l'astuce !).

Les dernières moutures de Linux, Suse ou Mandriva sont des pâles copies de Windows XP, en moins chouette visuellement, et en plus complexe à faire marcher. Esthétiquement ça fait un peu Matrix, parfois l'interface graphique disparaît (lancement et extinction par exemple) et on voit des trucs défilés dans une console, très geek mais pas très "end user" donc. Toutefois il faut saluer les gros efforts de ces dernières années pour rendre Linux utilisable, mais on l'impression d'un truc "cheap", on voit bien que c'est gratuit quoi... Surtout quand on utilise Vista, dont on peut dire ce qu'on veut, mais quand on en a pris l'habitude, c'est autrement plus joli et plus agréable que XP ou les distribs Linux. Les fous de la customisations diront qu'ils peuvent installer des bureaux super chouettes sous Linux, c'est vrai. Mais que d'effort pour arriver à faire, de façon compliquée, ce qu'un Vista fait de base de façon simple... Reste que la concurrence Linux est une bonne chose, et les essais que je vous conte ici sont la preuve que j'attache de l'importance à cette alternative même si elle est loin de me convaincre, pour le moment, de reformater toutes mes machines sous Linux.

Mais bon, le truc ce n'était pas de tester Linux ni de donner mon avis sur la question, Linux est un monde fermé, si un "vendu" à la cause Microsoft fait des réserves c'est forcément qu'il est corrompu et donc que son avis n'a pas d'intérêt. Les Linuxiens ne lisent donc de toute façon pas les avis des non linuxiens sur Linux. Non, mon but était d'installer MONO et de faire des tests. De ce côté là c'est concluant. Juste une chose qu'il me reste à creuser, la gestion des fenêtres (l'équilavent de Windows Forms) en tout cas dans la version dont je dispose, utilise un truc vraiment très différent donc pas portable du tout. Il va falloir que je regarde s'il existe une émulation des Windows Forms sinon cela limite la compatibilité entre .NET et MONO aux libs de classes. C'est déjà pas mal, mais ça me semble trop juste. [Edit:] Il y a bien un System.Windows.Forms dans les libs installées, mais point de designer dans l'IDE.. Il semble qu'il en existe un, mais j'ai pas tout pigé comment l'obtenir, est ce un truc à part, un plugin, en tout cas ça demande d'obtenir le source de MONO par svn et de tout recompiler.. Brrr. L'esprit Linux c'est, il faut l'avouer, très très éloigné des "user experiences" de MS ! [/Edit]

Voilà pour cette petite histoire, qui, au fil des paragraphes vous aura peut-être donné l'envie de tenter l'expérience aussi, en utilisant directement les bonnes solutions et sans perdre de temps :-)

avant de lâcher mon traditionnel "Stay Tuned!" voici une petite image, ça fait toujours plaisir :

 

OpenSuse 11.0 avec MonoDevelop

 

Sous les pavés la plage... Chez moi c'est vrai, mais en plus sous la fenêtre OpenSuse il y a aussi le bureau XP avec VirtualBox (et l'icone de lancement de VS 2008 mon compagnon de tous les jours) ! Cool isn't it ?

Alors... pour d'autres aventures : Stay Tuned !

 

[EDIT:] Comme j'ai réussi à faire pareil sous Mandriva, une petite image aussi ![/EDIT]

De l'intérêt d'overrider GetHashCode()

Les utilisateurs de Resharper ont la possibilité en quelques clics de générer un GetHashCode() et d'autres méthodes comme les opérateurs de comparaison pour toute classe en cours d'édition. Cela est extrêment pratique et utile à plus d'un titre. Encore faut-il avoir essayer la fonction de Resharper et s'en servir à bon escient... Mais pour les autres, rien ne vient vous rappeler l'importance de telles fonctions. Pourtant elles sont essentielles au bon fonctionnement de votre code !

GetHashCode()

Cette méthode est héritée de object et retourne une valeur numérique sensée être unique pour une instance. Cette unicité est toute relative et surtout sa répartition dans le champ des valeurs possibles est inconnue si vous ne surchargez pas GetHashCode() dans vos classes et structures ! Il est en effet essentiel que le code retourné soit en rapport direct avec le contenu de la classe / structure. Deux instances ayant des valeurs différentes doivent retourner un hash code différent. Mieux, ce hash code doit être représentatif et générer le minimum de collisions...

Si vous utilsez un structure comme clé d'une Hashtable par exemple, vous risquez de rencontrer des problèmes de performances que vous aurez du mal à vous expliquer si vous n'avez pas conscience de ce que j'expose ici...
Je ne vous expliquerais pas ce qu'est un hash code ni une table Hashtable, mais pour résumer disons qu'il s'agit de créer des clés représentant des objets, clés qui doivent être "harmonieusement" réparties dans l'espace de la table pour éviter les collisions. Car en face des codes de hash, il y a la table qui en interne ne gère que quelques entrées réelles. S'il y a collision, elle chaîne les valeurs.
Moralité, au lieu d'avoir un accès 1->1 (une code hash correspond à une case du tableau réellement géré en mémoire) on obtient plutôt n -> 1, c'est à dire plusieurs valeurs de hash se partageant une même entrée, donc obligation de les chaîner, ce que fait la Hashtable de façon transparente mais pas sans conséquences !

Il découle de cette situation que lorsque vous programmez un accès à la table de hash, au lieu que l'algorithme (dans le cas idéal 1->1) tombe directement sur la cellule du tableau qui correspond à la clé (hash code), il est obligé de parcourir par chaînage avant toutes les entrées correspondantes... De là une dégration nette des performances alors qu'on a généralement choisi une Hashtable pour améliorer les performances (au lieu d'une simple liste qu'il faut balayer à chaque recherche). On a donc, sans trop le savoir, recréé une liste qui est balayée là où on devrait avoir des accès directs...

La solution : surcharger GetHashCode()

Il existe plusieurs stratégies pour générer un "bon" hash code. L'idée étant de répartir le plus harmonieusement les valeurs de sorties dans l'espace de la table pour éviter, justement, les collisions de clés. Ressortez vos cours d'informatique du placard, vous avez forcément traité le sujet à un moment ou un autre ! Pour les paresseux et ceux qui n'ont pas eu de tels cours, je ne me lancerais pas dans la théorie mais voici quelques exemples d'implémentations de GetHashCode() pour vous donner des idées :

La méthode "bourrin"

Quand on ne comprends pas forcément les justifications et raisonnements mathématiques d'un algorithme, le mieux est de faire simple, on risque tout autant de se tromper qu'en faisant compliqué, mais au moins c'est facile à mettre en oeuvre et c'est facile à maintenir :-)

Imaginons une structure simple du genre :

public struct MyStruct
{
   
public int Entier { get; set; }
   
public string Chaine { get; set; }
   
public DateTime LaDate { get; set; }
}

Ce qui différencie une instance d'une autre ce sont les valeurs des champs. Le plus simple est alors de construire une "clé" constituée de toutes les valeurs concaténées et séparées par un séparateur à choisir puis de laisser le framework calculer le hash code de cette chaîne. Toute différence dans l'une des valeurs formera une chaine-clé différente et par conséquence un hash code différent. Ce n'est pas super subtile, mais ça fonctionne. Regardons le code :

public string getKey()
{
return Entier + "|" + Chaine + "|" + LaDate.ToString("yyyyMMMddHHmmss"); } public override int GetHashCode() {return getKey().GetHashCode(); }

J'ai volontairement séparé la chose en deux parties en créant une méthode getKey pour pouvoir l'afficher.

La sortie (dans un foreach) de la clé d'un exemple de 5 valeurs avec leur hash code donne :

1|toto|2008juil.11171952 Code: -236695174
10|toto|2008juil.11171952 Code: -785275536
100|zaza|2008juil.01171952 Code: -684875783
0|kiki|2008sept.11171952 Code: 888726335
0|jojo|2008sept.11171952 Code: 1173518366 

La méthode Resharper

Ce merveilleux outil se propose de générer pour vous la gestion des égalités et du GetHashCode, laissons-le faire et regardons le code qu'il propose (la structure a été au passage réécrite, les propriétés sont les mêmes mais elles utilisent des champs privés) :

D'abord le code de hachage :

public override int GetHashCode()
{
   unchecked
   {
      int result = entier;
      result = (result*397) ^ (chaine !=
null ? chaine.GetHashCode() : 0);
      result = (result*397) ^ laDate.GetHashCode();
      return result;
   }
}

On voit ici que les choix algorithmiques pour générer la valeur sont un peu plus subtiles et qu'ils ne dépendent pas de la construction d'une chaîne pour la clé (ce qui est consommateur de temps et de ressource).

Profitons-en pour regarder comment le code gérant l'équalité a été généré (ainsi que le support de l'interface IEquatable<MyStruct> qui a été ajouté à la définition de la structure)  - A noter, la génération de ce code est optionnel - :

public static bool operator ==(MyStruct left, MyStruct right)
{
return left.Equals(right); }

public static bool operator !=(MyStruct left, MyStruct right)
{
return !left.Equals(right); }

public bool Equals(MyStruct obj)
{ return obj.entier == entier && Equals(obj.chaine, chaine) && obj.laDate.Equals(laDate); }

public override bool Equals(object obj)
{
   
if (obj.GetType() != typeof(MyStruct)) return false;
    return Equals((MyStruct)obj);
}

Bien que cela soit optionel et n'ait pas de rapport direct avec GethashCode, on notera l'intérêt de la redéfinition de l'égalité et des opérateurs la gérant ainsi que le support de IEquatable. Une classe et encore plus une structure se doivent d'implémenter ce "minimum syndical" pour être sérieusement utilisables. Sinon gare aux bugs difficiles à découvrir (en cas d'utilisation d'une égalité même de façon indirecte) !

De même tout code correct se doit de surcharger ToString(), ici on pourrait simplement retourner le champ LaChaine en supposant qu'il s'agit d'un nom de personne ou de chose, d'une description. Tout autre retour est possible du moment que cela donne un résultat lisible. Ce qui est très pratique si vous créez une liste d'instances et que vous assignez cette liste à la propriété DataSource d'un listbox ou d'une combo... Pensez-y !

Conclusion

Créer des classes ou des structures, si on programme sous C# on en a l'habitude puisque aucun code ne peut exister hors de telles constructions. Mais "bien" construire ces classes et structures est une autre affaire. Le framework propose notamment beaucoup d'interfaces qui peuvent largement améliorer le comportement de votre code. Nous avons vu ici comment surcharger des méthodes héritées de object et leur importance, nous avons vu aussi l'interface IEquatable. IDisposable, INotityPropertyChanged, ISupportInitialize, et bien d'autres sont autant d'outils que vous pouvez (devez ?) implémenter pour obtenir un code qui s'intègre logiquement au framework et en tire tous les bénéfices.

Bon dev, et Stay Tuned !

La bombe Parallèle ! PLINQ, et PCP Parallel Computing Platform

Le framework .NET n'en finit pas d'innover ! La bombe du jour c'est PLINQ et PCP.

Vous ne connaissez pas encore ? Normal, ce n'est même pas encore sorti, juste en preview...

Pour résumer en un mot : Révolutionnaire. Comme LINQ.

L'objectif

Vous l'avez certainement remarqué comme tout geek qui se respecte, depuis quelques années nos amis fondeurs ne se battent plus à coup de Ghz pour promouvoir leurs microprocesseurs... Les gammes se multiplient et surtout les coeurs ! Et le temps où le concepteur d'un soft trop lent pouvait toujours dire "je suis en avance c'est tout, avec la dernière génération de processeur mon soft est super rapide!", ce temps là est fini, révolu.

Aujourd'hui un soft lent sur un dual core sera peut être même encore plus lent sur un quad core d'une autre série (il y a plein de séries de processeurs on ne s'y retrouve d'ailleurs plus pour choisir...).

Donc, pour qu'un soft soit plus rapide, inutile de compter sur la prochaine génération de processeur qui doublera la fréquence d'horloge. Aujourd'hui on double le nombre de coeurs, c'est tout. un, puis deux, les quatre coeurs se banalisent, Intel avait annoncé (le tiendront ils) que d'ici peu de temps on atteindrait les 100 coeurs... Mais pour tirer partie de tous ces coeurs finalement pas si rapide, une seule solution le parallélisme !

Parallèlisme et programmation

Seulement voilà, pour bénéficier de tous ces coeurs il faut programmer selon un mode parallèle. Le plus "simple" consiste à faire du multi-threading en laissant l'OS répartir les threads entre les coeurs, mais pour plein de raison (je vous laisse y réfléchir) cela n'est pas forcément optimal. Un simple exemple : une tâche un peu lourde, vous n'avez pas besoin de 50 threads, vous avez une grosse bonne boucle qui traite une image pour faire du morphing par exemple. Allez programmez ça en créant plusieurs threads ! Si vous y arriver sans y engloutir des semaines de mise au point, je veux bien manger mon chapeau (il faudra fournir le chapeau je n'en porte pas, et si vous le fournissez, ça ne sera pas le mien, donc je ne le mangerais pas. On ne m'a pas si facilement...) !

Car en effet, multiplier les threads n'est pas toujours réalisable, en tout cas simplement. Découper une tâche en sous tâches parallélisables est en réalité tellement complexe, surtout de façon générique, que les langages et les OS sont bien à la traîne par rapport aux microprocesseurs. A la traîne ? Pas tous... Car sous .NET il va falloir compter avec PLINQ et PCP !

PLINQ et PCP

En très gros il s'agit d'extensions du framework .NET qui permet de paralléliser à peu près tout traitement sans presque aucune programmation particulière. Oui vous avez bien lu...

Ainsi, PLINQ est une extension parallèle de LINQ. Une requête LINQ comme :

var result = from c in liste where c.champ==5 orderby c.autreChamp select c; // version simple

devient en parallèle :

var result = from c in liste.AsParallel() where c.champ==5 orderby c.autreChamp select c; // parallélisée

Et c'est tout ! D'un seul coup la requête devient parallèle et s'executera sur tous les coeurs disponibles à la fois, le boulot étant découpé, les threads créés, et tout le reste, de façon automatique par PLINQ. Un rêve ? Non, pas besoin de se pincer, c'est déjà en preview à télécharger ! (voir en fin de billet).

Bien entendu il existe beaucoup de variantes et une syntaxe appropriée pour, par exemple, modifier le nombre de coeurs utilisés et plein d'autres nuances subtiles.

La parallélisation de LINQ est une pure merveille, mais il y a aussi PCP qui est le framework plus généraliste permettant de paralléliser des foreach, des for, etc, juste avec un mot clé. Il existe là aussi toute une panoplie de méthodes qu'il va falloir découvrir. Mais vous avouerez que le bond en avant est aussi fantastique que celui de LINQ par exemple. Je ne pense sincèrement pas que quiconque ayant utilisé LINQ ne voudra dans l'avenir d'un langage ne supportant pas une telle extension. Il y a eu un avant LINQ, il y aura forcément un après LINQ, mais on ne traversa pas le temps sans faire référence à cette nouvelle ère, au sens premier, le clou d'airain planté par le prêtre dans la mur du temple de la Rome antique pour marquer un événement crucial d'une telle importance qu'une nouvelle série d'années commençait.

PLINQ et PCP marqueront de la même façon une nouvelle ère.

Faire joujou ?

Je reconnais bien le geek qui sommeille en vous ! Oui c'est possible ! Il existe une CTP sortie en juin et qui est téléchargeable sur le site MSDN consacré à la programmation parallèle. Vous y trouverez de nombreuses informations qui complèteront facilement la très rapide présentation faite ici.

Le site : http://msdn.microsoft.com/fr-fr/concurrency/

... Vous êtes encore là ? Mais foncez je vous dit, c'est l'éclate du geek ce truc ! (mais pensez à revenir hein.. Stay Tuned !) Wink

[EDIT: lire aussi ce billet plus récent qui donne l'adresse d'un article sur PFX et de plusieurs Webcasts]

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 !