Cette session est présentée par Roch Baduel, MVP, architecte chez MCNext. L’amphi bleu continue à se remplir, et visiblement le sujet intéresse fortement puisque la salle est déjà quasi-bondée alors que c’est une des plus grandes.
La version 4.5 est une mise à jour par dessus la 4.0. Donc, comme pour la version 2.0 avec les versions 3.0 et 3.5: il n’y a pas de changement majeur de la CLR, mais juste des ajouts de fonctionnalités. La 4.5 pourra être déployée par Windows Update. Par contre, attention, il y a des modifications dans les classes du 4.0, même si la compatibilité est a priori bonne.
Asynchronisme
Le gros point important est la programmation asynchrone et parallèle : les briques de bases qui étaient auparavant disponibles en Community Technology Preview seront désormais inclues dans le Framework. Les Tasks sont étendues dans le 4.5 par rapport à leur version 4.0. Les DataFlow également seront intégrées.
L’asynchrone a évolué de l’Asynchronous Programming Model (méthodes Begin / End) au Event-based Asynchronous Programming (Callback de type Completed, méthode suffixée par Async), et maintenant en Task-based Asynchronous Programming (async / await).
On écrit typiquement Task<string> tache = webClient.DownloadStringTaskAsync(url); D’après ce que j’ai compris (mais je n’ai pas de Visual Studio 11 sur cette machine, donc à vérifier), il s’agit d’une des méthodes d’extension de WebClient qui ont été ajoutées lors du passage au .NET 4.5.
Cette ligne crée une tâche, à savoir un objet qui porte un traitement. C’est le gestionnaire de pool de thread qui décide quand lancer ce traitement. S’il se trouve qu’il n’a rien à faire, il peut le lancer tout de suite, mais il peut tout aussi bien le retarder et le traiter bien plus tard, selon son bon vouloir. C’est au code appelant de faire ce qui est nécessaire pour que la valeur soit calculée lors de son besoin.
Pour cela, on dispose de plusieurs méthodes de programmation. Tout d’abord, on peut utiliser une ligne comme tache.Result pour revenir dans un modèle synchrone et bloquant. Le fait d’appeler Result va demander l’exécution de la tâche, et attendre sa fin pour retrouver le résultat de sortie, qui sera du type qui avait été utilisé pour la classe générique Task<T>.
Une autre méthode qui est mieux est de chaîner une tâche avec un traitement suivant. Comme ça, on ne dit pas explicitement de calculer et on laisse le choix au moteur du moment opportun. tache.ContinueWith(task => string html = task.Result); permet de réaliser ceci. Bien sûr, il faut alors ne pas oublier de faire un Start() sur la tâche…
Et sinon, il y a la nouvelle grammaire string html = await task.Result; Je ne suis pas encore suffisamment au point sur les mots-clés async et await pour expliquer ceci proprement, mais en gros, il s’agit de “sucre syntaxique” et le compilateur se charge de recréer en arrière plan tout ce qui est nécessaire pour le fonctionnement asynchrone expliqué plus haut.
Les méthodes doivent être déclarées en async pour pouvoir être utilisées non pas comme des exécuteurs immédiats de traiement, mais plutôt comme fournisseur d’un résultat dans le futur,. Et logiquement, elles doivent dans ce cas renvoyer ou bien un Task,ou bien void. Au final, pour transformer une méthode synchrone en asynchrone, il suffit de mettre le mot clé async dans la signature, et de remplacer tous les appels de fonction par leur équivalent suffixé par Async, en mettant juste devant l’appel un mot clé await.
Un des gros avantages de l’asynchrone pour la réactivité des UI est qu’avec ce mode, on est de retour dans le contexte de la thread GUI, donc on peut facilement mettre à jour l’affichage sur le retour de la tâche. Il faut toutefois utiliser this.Dispatcher.InvokeAsync(), de façon que la modification de l’IHM soit bien réalisée sur le thread associé.
La démo n’est malheureusement pas super réussie, et surtout n’explique pas réellement la différence entre le cas asynchrone où les retours sont mélangés, et le second cas où l’écriture fait que le traitement est asynchrone, mais tout de même chaîné dans l’ordre. C’est dommage, parce que ça donnerait une meilleure compréhension de comment ça marche, plutôt que de s’arrêter sur l’aspect “magique” de la techno… Cette expression a également été trop entendue à mon sens lors des TechDays : très souvent, les orateurs indiquaient qu’à partir d’un certain moment, il suffisait de “laisser la magie opérer”. Je dis “trop entendue” parce qu’à mon sens, on est entre professionnels, et la magie est peut-être bonne pour le grand public, mais en tant que développeur, moi j’ai besoin de comprendre ce qu’il y a derrière, et je n’utiliserai pas une technologie Microsoft (ou n’importe quel autre éditeur, pour le coup) sans être raisonnablement au courant du fonctionnement interne de celle-ci. C’est une question de capacité à appliquer la technologie dans de bonnes conditions. Il y a un dicton comme “tout technologie suffisamment avancée est indifférenciable de la magie”. Dans notre cas, insister sur le fait que les technologies sont “magiques” me fait surtout imaginer qu’elles sont très complexes, et potentiellement donc fragiles…
Task
Des méthodes ont été ajoutées sur la classe Task, à savoir principalement Delay, WhenAny, Run, Yield, ContinueWith. A tester soi-même par contre, parce qu’aucune explication n’est donnée hormis un très rapide slide avec une ligne par fonction.
DataFlow
En .NET 4, traditionnellement, on part des données et on met en place le traitement. En DataFlow, l’idée est de faire dans l’autre sens, en créant un pipeline de traitement de données : on instancie un réseau de blocks, puis on injecte dedans de la donnée. On parle bien de réseau, et pas seulement de pipelines linéaires. Chaque block s’exécute de manière parallèle en utilisant le ThreadPool.
var ab = new ActionBlock<int>(i => { Process(i); });
for (int i = 0; i < 5; i++) ab.Post(i);
Il y a aussi des TransformBlock<TInput, TOutput>, des BroadCastBlock, JoinBlock, BufferBlock, etc.
using System.Threading.Tasks.Dataflow pour avoir accès à ces classes.
TransformationBlock<string, string> tb = new Transformation<string, string>(s => (if s.StartsWith(“a”) return Enumerable.Empty<string>() else return new string[] { s };);
Ensuite, il suffit de faire un LinkTo d’un bloc à l’autre, et de poster la donnée sur le premier bloc de la chaîne. Dans la démo, le dernier bloc fait un affichage, c’est un ActionBlock, mais il doit être possible de simplement donner en sortie un IEnumerable pour pouvoir travailler en mode pull comme avec Linq. J’essaierai de faire un article de blog plus complet avec un code fonctionnel reprenant tout ceci, mais là, je suis dans le train et LiveWriter est tout ce que j’ai pour retaper mes notes sous forme de blog.
Autres nouveautés
Sytem.Net.Http avec HttpClient, HttpRequestMessage, etc. : API de bas niveau pour HTTP.
MEF 2.0 : support des types génériques, composition basée sur des règles (les attributs ne sont plus obligatoires). Utilise RegistrationBuilder pour injecter des attributs. ReflectionContext : expose des types “altérés”. Ceci devra normalement nous permettre de travailler avec MEF de manière plus souple. Par exemple, tout objet avec un nom donné, ou implémentant une interface donnée, pourra être utilisé en injection, même s’il n’a pas été prévu pour cela dès le début.
Le concept de Portable Library, apparu pour garder en commun des libraires entre Silverlight et .NET, va être étendu à XBox, Windows Phone, etc.
WCF va enfin permettre de faire du Contract First : on pourra appeler svcutil /serviceContract pour générer le squelette de code à partir du WSDL. A voir si la montée en version se passe bien, toutefois, parce que c’est quelque chose de générer du code, mais c’est encore autre chose de le générer d’une manière qu’il puisse supporter une montée en version sans avoir à trop intervenir manuellement pour tout refaire. Normalement, les classes partielles devraient aider, et surtout, comme les services web sont en général exposés à l’externe et ont besoin d’une grande stabilité / compatibilité ascendante, il n’y aura normalement pas de souci, mais bon… J’attends de voir.
PasteXMLAsClasses permet, depuis Visual Studio, de créer automatiquement une classe de sérialisation à partir d’un XML quelconque. Sympa, même si ça tient plus du gagdet que du vrai outil de production. C’est plus à voir comme une sorte de snippet de génération de code dont le paramètre serait un XML exemple.
Du côté Workflow, les machines à état reviennent, et il est possible de mettre en place des règles en grammaire C#.
Le mécanisme de Ruban est inclus dans .NET 4.5 sur WPF. Pour le binding, un Delay est désormais possible, pour ne pas envoyer un tas de modifications lorsque l’utilisateur joue avec un slider pour arriver progressivement à la valeur qu’il souhaite, par exemple. IsVirtualizingWhenGrouping permet d’augmenter très fortement la performance sur le groupage dans un ListView en WPF. Il faudra toutefois vérifier que ça fonctionne bien avec des ruptures métier, de la pagination, etc.