Bienvenue à Blogs CodeS-SourceS Identification | Inscription | Aide

Pierrick's Blog

.Net "full power"

Guide de migration vers Workflow Foundation 4

Microsoft propose un guide de migration pour migrer vos workflows WF3 (framework .net 3.0, 3.5) vers WF4(framework .net 4.0)

http://www.microsoft.com/downloads/details.aspx?FamilyID=bd94c260-b5e0-4d12-93ec-53567505e685&displaylang=en

 

source : http://blogs.msdn.com/swiss_dpe_team/archive/2009/06/06/migration-guidance-for-wf-developers.aspx

Tetris on the cloud !

Il y a quelque temps (un peu plus de 2 ans 1/2), je testais WPF, WCF, WF et à cette occasion j’avais réalisé un jeu, une forme de “Tetris” mais multi-joueurs

image

Celui-ci était réalisé avec le framework 3.0.

Un framework et un servicepack plus tard j’ai donc décidé de le faire évoluer, et d’utiliser Windows Azure.

Architecture d’origine

Chaque joueur pouvant être “serveur”, toutes connexions entre les joueurs passe par le serveur.

image

Le serveur doit être connu de tout les participants. Le serveur est un service WCF  basé sur le protocole “net.p2p”. J’ai pour cela dérivé la classe “System.ServiceModel.PeerResolver”.

Inconvénients

  1. Le joueur serveur reçoit toute les connexions, si il “tombe” le jeu est terminé pour l’ensemble des joueurs
  2. autre inconvénient majeur de cette solution est l’ouverture d’un port sur toute les machines.
  3. Le joueur serveur est aussi avantagé, sa connexion avec le “serveur” est locale.
  4. Cette architecture était plutôt orientée LAN

Le but au départ était de montrer un exemple utilisant les nouvelles briques du framework 3.0.

Ce qu’apporte Windows Azure et le ServiceBus

Pour ce jeu, j’ai tout simplement remplacer toute la partie qui gère le p2p entre les joueurs, c’est le rôle de la plateforme Azure.  Si vous ne connaissez pas cette plateforme vous trouverez pas mal d’informations ici.

Le service utilisé dans ce jeu est le ServiceBus, et plus particulièrement la publication/souscription en multicast.

image

Tous les participants sont connectés sur un même point central (défini par une uri), et ainsi peuvent envoyer/recevoir sans avoir besoin de se connaitre mutuellement.

ainsi la nouvelle architecture est :

image

ceci est l’état du système lors d’une phase de jeu. Mais que ce passe-t-il avant….au commencement du jeu…

image

  1. Le joueur qui désire être serveur, envoi un message “SendRequest” avec l’uri pour répondre (ici : sb://…/Guid/Acceptor)
  2. les joueurs souhaitant participer se connectent au service d’acceptation pour obtenir une autre Uri, cette fois celle du service multicast pour la nouvelle partie. celle-ci est générée dynamiquement à chaque nouvelle partie.
  3. les joueurs ayant accepter l’invitation du serveur se retrouvent connectés ensemble sur un point qu’il sont seuls à connaitre.

 image

ainsi plusieurs parties peuvent avoir lieu au sein de la même “solution“ Windows Azure

Les différentes interfaces des différents services

La gestion de la connexion (service représenté en orange ci-dessus), ce service permet au joueurs de se voir mutuellement

[ServiceContract(Namespace = Constants.ContractNamespace)]
public interface IPeerConnexionService
{
    #region connection managment
   
/// <summary>
    /// used to join, others player should respond with hello 
    /// </summary>
    [OperationContract(IsOneWay = true)]
   
void Join(PeerPlayer player);

   
/// <summary>
    /// used to leave the room
    /// </summary>
    [OperationContract(IsOneWay = true)]
   
void Leave(PeerPlayer player);

   
/// <summary>
    /// the new player receive one "Hello" from all other players
    /// </summary>
    [OperationContract(IsOneWay = true)]
   
void Hello(PeerPlayer player);

   
/// <summary>
    /// used to send a ping to a specific player
    /// </summary>
    [OperationContract(IsOneWay = true)]
   
void Ping(PeerPlayer from, PeerPlayer to);

   
/// <summary>
    /// used to respond from ping
    /// </summary>
    /// <param name="from"></param>
    /// <param name="to"></param>
    [OperationContract(IsOneWay = true)]
   
void Pong(PeerPlayer from, PeerPlayer to);

   
/// <summary>
    /// 
    /// </summary>
    /// <param name="player"></param>
    /// <param name="acceptor">uri of the acceptor service</param>
    [OperationContract(IsOneWay = true)]
   
void SendGameRequest(PeerPlayer player, Uri acceptor);
    #endregion
}

 

Le service que chaque joueur publie pour accepter les autres joueur dans son espace de jeu. ce service à la particularité d’être un service “wcf” standard, contrairement aux autres qui sont “multicast”

[ServiceContract(Namespace = Constants.ContractNamespace)]
public interface IAcceptorService
{
    [
OperationContract]
   
Uri IWouldLikeToplay(PeerPlayer player);
}

 

service utiliser pendant la phase de jeu (en vert sur le schéma ci-dessus)

    [ServiceContract(Namespace = Constants.ContractNamespace)]
   
public interface ITetrisGameService
    {
        [
OperationContract(IsOneWay = true)]
       
void SendBoard(Guid idplayer, byte[] board, int lengthDimension0, int lengthDimension1);

        [
OperationContract(IsOneWay = true)]
       
void SetTimerInterval(Guid idplayer, double newValue);

        [
OperationContract(IsOneWay = true)]
       
void GameOver(Guid idplayer);

        [
OperationContract(IsOneWay = true)]
       
void SendOption(Guid from, Guid to, byte option);

        [
OperationContract(IsOneWay = true)]
       
void SendScore(Guid idplayer, int score);

        [
OperationContract(IsOneWay = true)]
       
void HideBoard(Guid idplayer, bool value);

        [
OperationContract(IsOneWay = true)]
       
void GameStarting(Guid idCoordinator, int nbPlayer);

        [
OperationContract(IsOneWay = true)]
       
void PlayerReady(Guid idplayer);

        [
OperationContract(IsOneWay = true)]
       
void ImIn(Guid idplayer);
    }

Premières impressions

  • extrêmement simple de publier un service mondialement ! ;-)
  • c’est du WCF, donc la configuration est identique à ce que l’on connais déjà
  • la publication des services sur le cloud reste assez lente (plusieurs secondes par service)
    • il ne faut pas oublier que le framework Windows Azure est en CTP
    • par contre ensuite (dans le cadre du tetris) une fois la connexion établie,  les échanges sont fluides.
    • vous le remarquerez en cliquant sur “connect to mesh” dans le menu “game”

 

Utiliser ce jeu

Téléchargez l’installation depuis codeplex : http://geniustetris.codeplex.com/

une fois installer il faut modifier le fichier de configuration de l’application “geniustetris.exe.config”, plus précisément  les “credentials” pour se connecter à windows azure

    <behaviors>
      <
endpointBehaviors>
        <
behavior name="password">
          <
transportClientEndpointBehavior credentialType="UserNamePassword">
            <
clientCredentials>
              <
userNamePassword userName="GeniusTetris" password="****" />
            </
clientCredentials>
          </
transportClientEndpointBehavior>
        </
behavior>
      </
endpointBehaviors>
    </
behaviors>

Remplacer “GeniusTetris” par le nom votre solution Windows Azure, et le mot de passe par le votre. n’oubliez pas de modifier dans le jeu les options “réseaux” et de fournir le nom de la solution à utiliser.

il est possible d’utiliser d’autres moyens d’authentification  comme “CardSpace”, cela fonctionne aussi.

vous voulez jouer ? alors rendez vous sur http://geniustetris.codeplex.com/

 

bon amusement !

“Axum” un langage pour le faciliter l’écriture d’applications parallèles

un nouveau projet à vu le jour sur dev labs, il s’agit d’un langage Axum

Curieux.., j’ai installer le msi, qui me donne dans visual studio 2008, un type de projet supplémentaire :

image

 

curieux de connaitre ce qui avait derrière… j’ai suivi le webcast présent sur devlabs, et réalisé ma librairie mathématiques….

using System;
using System.Collections.Generic;
using System.Text;

namespace AxumMath
{
       
public channel IntOperator
    {
       
input int X;
       
input int Y;
       
output int Z;
    }
    
       
public agent AdderAgent : channel IntOperator
    {
       
public AdderAgent()
        {
           
while(true)
            {                
                Z
<-- (receive(X) + receive(Y));
            }
        }
    }
}

qui fait une addition….

mes premiers sentiments au sujet de ce langage sont :

  • effectivement on commence par décrire le message que l’on souhaite traiter, ensuite l’agent qui traite le message. mais je trouve cette description encore un peu trop verbeuse...

je pense qu’il est possible d’être un peu plus succinct.

public message IntOperator
{
   
input int X;
   
input int Y;
   
output int Z;
}

public agent AdderAgent
{
       
receive<IntOperator
        {
                Z
<-- X + Y;
        }
}

évidemment on peut imaginer une multitude d’écriture…

  • ce sucre syntaxique génère beaucoup de code(voir plus bas), mais en revanche cela simplifie l’écriture des applications
  • ce langage s’approche du domaine fonctionnel mais sans l’être.
  • le type du message est très “parlant”, on sait immédiatement ce qui est en entrée(input) et en sortie(ouput).

Revenons à notre agent et channel d’origine, et voyons ce que devient le code une fois compilé :

image

en réalité le mot clé channel, génère une classe avec les données qui dérive d’un classe “Channel”, mais pas seulement...

image

les données X,Y, Z, ne sont pas membres de la classe sous forme d’entier, mais sous forme d’objet complexe. Et oui fournir un langage pour concevoir des applications parallèles n’est pas simple…, surtout si le langage doit être accessible aux plus grands nombre,tout en rendant abstrait la distribution/parallélisme de “calculs utilisateur”.

en creusant un peu plus, cette implémentation de Axum utilise WCF pour faire transiter les données “input” :

image

il est clair que pour faire une addition, c’est peu être un peu trop…

La programmation parallèle est de plus en plus présente, tout les acteurs proposent des frameworks, langages, outils pour développer plus facilement des applications parallèles.

  • Outils
    • intel parallel studio
    • visual sutdio 2010
  • Framework
    • Parallel Fx (.Net)
    • PLINQ (.Net)
    • JPPF (java)
  • Langages
    • Arche (successeur de Polygoth)
    • Erlang
    • Axum

cette liste n’est pas exhaustive, mais la tendance est là !

“Code canvas” / Deep Zoom for code

Microsoft Research nous présente leur travaux sur l’interface homme machine dans le domaine de la programmation. Ce projet à pour but de visualiser votre code sous un autre angle...

Aujourd’hui dans Visual studio nous avons plusieurs manière de “voir” notre code:

  • l’éditeur de code (et oui parfois c’est pratique pour tapez du code…)
  • l’explorateur de classes
  • l’explorateur de solution
  • l’explorateur d’objet
  • les differents designers (windows forms, WPF, WF,…)
  • ...

et bien il y a un petit nouveau qui pointe son nez !

image

en fonction du zoom on voit les dépendances

  • entre les assemblies
  • entre les fichiers sources
  • entre les classes
  • entre les propriétés des classes

une superbe application du “deep zoom”, à voir absolument !

 

un autre exemple issue du webcast sur le CCI (Common Compiler Infrastructure)

image 

un conseil : regarder ce webcast !

les liens :

 

un projet à suivre asbolument !

[ccimetadata] un framework pour écrire des lecteurs/écrivains pour MSIL

ccimetadata est un projet disponible sur codeplex  qui permet d’écrire des lecteurs /ecrivains pour MSIL et les fichiers de symboles.

 

mais à quoi cela peut-il bien servir ?

FxCop, ILMerge sont  des applications cible de ce framework. Mais si je vous dit “programmation orienté aspect”… ça vous parle ?

Effectivement ce type de framewrk peut être utilisé pour faire du tissage d’aspect.

et devinez qui participe à ce projet ??

-> jonathan de halleux, acteur sur les projets Pex, QuickGraphcci, codecontracts, Chess et  le créateur de MBUnit

 

encore un projet MsResearch à suivre !

DevLabs: CodeContracts

un petit nouveau est apparu sur DevLabs. il s'agit de CodeContracts, un framework de vérification. Il vous suffit de spécifier sur vos API, des pré-conditions, post-conditions, et invariants sous forme de contrats. Ces contrats peuvent ensuite utilisés pour une vérification statique (à la compilation), pour une vérification au runtime (à l'éxécution), mais ces contrats peuvent être utilisés aussi  pour les tests, et bien sur pour la documentation. En effet je pense que ce dernier point permet de garder toujours une documentation en adéquation avec le code (puisqu'elle en est issue..).

un projet à suivre tout comme Pex....

Installation de Windows 7 dans un fichier vhd

il existe un bon tutoriel pour installer Windows 7 dans un fichier .vhd, c'est ici : http://blogs.msdn.com/editeurs_de_logiciels/pages/installer-windows-7-beta-sans-casser-votre-machine.aspx

cependant si comme pour moi cette procédure ne fonctionne pas correctement (i.e le chargement du driver disk.sys, provoque un reboot au démarrage du sytème, et donc la machine reboot indéfiniment).

voici une solution qui fonctionne dans mon cas.

Modifier l'étape 7 :

7. Nous allons ensuite créer un disque virtuel (*.vhd), pour ensuite installer Windows 7, en utilisant l’utilitaire diskpart.exe, suivi des commandes suivantes pour créer un disque virtuel.

   create vdisk file=c:\win7beta.vhd maximum=32000

   select vdisk file=c:\win7beta.vhd

   attach vdisk

   exit

La commande create vdisk file=”xxx” maximum=yyy permet de fixer la taille de votre disque virtuel. Prévoyez suffisamment d’espace pour vos applications. Dans l’exemple, j’ai choisi de créer un disque virtuel représenté par c:\win7beta.vhd de taille 32 Go.

le but est de placer le fichier .vhd dans un sous répertoire et non à la racine (ne me demandez pas pourquoi...mystère).

donc avant d'executer dispkart, il faut d'abord créer un répertoire

md c:\vhd

ensuite le reste identique au répertoire près

7. Nous allons ensuite créer un disque virtuel (*.vhd), pour ensuite installer Windows 7, en utilisant l’utilitaire diskpart.exe, suivi des commandes suivantes pour créer un disque virtuel.

   create vdisk file=c:\vhd\win7beta.vhd maximum=32000

   select vdisk file=c:\vhd\win7beta.vhd

   attach vdisk

   exit

La commande create vdisk file=”xxx” maximum=yyy permet de fixer la taille de votre disque virtuel. Prévoyez suffisamment d’espace pour vos applications. Dans l’exemple, j’ai choisi de créer un disque virtuel représenté par c:\vhd\win7beta.vhd de taille 32 Go.

 

Cette méthode possède l' avantage de ne pas toucher au système hôte.

Un Silverlight tour, sous forme de formation

Les sociétés RunAtServer Consulting et Octo Technology organisent une halte à Paris du Silverlight Tour le 19, 20, 21 Janvier 2009.

c' est Laurent Duveau (MVP canadien) qui donne cette formation à Paris. Si vous êtes intéressé c' est ici.

Small basic - un projet intéressant

Connaissez vous ce projet : small basic ?

c' est un projet hébergé sur msdn devLabs, dont le but est d' apprendre la programmation aux débutants (faq).

image 

l' interface est bluffante (en WPF évidement), Mais le coté intéressant de ce projet est l' approche utilisateur :

  • un langage avec 14 mots clés (donc simple)
  • Approche très contextuel
  • Une aide au fil de l' eau
  • Le clic droit dans l' éditeur est absent, mais dans ce cadre inutile
  • Intellisense limité à 7 élements visible, mais la liste est circulaire
  • Ici l' utilisateur ne fait que consommer des objets, il n' a pas la possibilité d' en définir.

Conclusion :

il possible de faire un IDE avec une dizaine de "commandes".

Dans bien des cas on désire offrir à une catégorie de personnes, un éditeur de script orienté "métier". Je pense que ce projet est une bonne source d' inspiration.

 

Une autre question me chatouille quelques neurones...

Il existe un éditeur de code en WPF ?

- oui

Ce projet est basé sur une partie d' un framework qui à fait son apparition à la PDC "Olso SDK". Tiens, tiens... serait-ce un avant goût du prochain Intellipad ?

Training KIT .NEt 4.0 déjà dispo !

au hazard des mes lectures..., j'ai découvert via ce blog, et qu'il était possible de télécharger le training kit pour Visual Studio 2010.

et oui on en oublierai presque que le Framework 3.5 sp1 viens de sortir il y a 3 mois...

Pacman Silverlight 2 RTW (enfin !)

je n'avais pas encore eu le temps de migrer ce petit jeu pour la version RTW de Silverlight 2.0. C'est dorénavant chose faite :

Amusez vous bien !

pour ceux qui ont encore le client Silverlight Béta 2, l'ancien fonctionne toujours mais à cette adresse : ici

Comment faire un récepteur de messages à la Erlang en C#

Un autre langage utilise lui aussi le pattern matching pour la reception des "messages" dans la communication inter-process, c'est Erlang. En effet en Erlang pour recevoir des messages on utilise ce type de syntaxe :

receive
    Pattern1 ->
      Expressions1;
    Pattern2 ->
      Expressions2;
end

un exemple :

receive
    {rectangle, Width, Ht} ->
        io:format("Aera of rectangle is ~p~n", [Width * Ht]);
    {circle, R} ->
        io:format("Aera of circle is ~p~n", [3.14159 * R * R]);
end

On remarque aisément que cela ressemble à un switch. tiens, tiens, ... ni aurait-il pas une ressemblance avec ce post(comment faire un pattern matching en C# comme celui de F#) ?

Mais comme on parle de messages, on peut imaginer un système de "publish-subscribe" basé sur le Type du message.

on pourrait ainsi écrire en C# :

    class Program
    {
       
class MyRectangle
        {
           
public int Width { get; set; }
           
public int Ht { get; set; }
        }

       
class MyCircle
        {
           
public int R { get; set; }
        }


       
static void Main(string[] args)
        {
           
PubSub.Subscribe<MyRectangle>(r =>
            {
               
Console.WriteLine("Area of Rectangle is {0}", r.Width * r.Ht);
            });
           
PubSub.Subscribe<MyCircle>(c =>
            {
               
Console.WriteLine("Area of Circle is {0}", c.R * c.R * 3.14159);
            });

           
//publication 
            new MyRectangle() { Width = 6, Ht = 10 }.Publish();
           
new MyCircle() { R = 23 }.Publish();

           
Console.ReadLine();
           
return;
        }
    }

à l' exécution

image

Le principe  ?

Associer un Type (i.e MyRectangle) avec une déléguée (i.e r => ...).

public static Guid Subscribe<T>(Action<T> caseAction)
{
   
return Subscribe(string.Empty, caseAction);
}

public static Guid Subscribe<T>(string topic, Action<T> onMessageReceived)
{
   
Guid result = Guid.NewGuid();
   
lock ((_subscriptions.Keys as ICollection).SyncRoot)
    {
       
Action<object> current;
       
if (_subscriptions.TryGetValue(topic, out current))
        {
           
//add the new delegate to current subscribers list
            var currentCase = current.Target as CaseActionHolder<object>;
           
var newCase = current.Case(result, onMessageReceived);
            currentCase.PreviousID = result;
            ((
CaseActionHolder<object>)newCase.Target).NextAction = current;
            _subscriptions[topic] = newCase;
        }
       
else
        {
           
//creates case
            current = current.Case(result, onMessageReceived);
            _subscriptions.Add(topic, current);
        }
        _subscriptionsById.Add(result, topic);
    }
   
return result;            
}

Dans mon implémentation, j'ai ajouté une notion (topic), le sujet qui permet d'avoir à l'utilisation un découpage logique des abonnements. De plus il est possible de se désabonner grâçe au guid renvoyé lors de l'abonnement.

Le "switch-case" pour dispatcher le message ressemble beaucoup à celui de mon post sur le pattern matching F#:

static Action<object> Case<TArg>(this Action<object> fn, Guid id, Action<TArg> caseAction)
{
   
var thisCase = new CaseActionHolder<object> { ID = id };
    thisCase.Action = (arg) =>
    {
       
if (typeof(TArg).IsAssignableFrom(arg.GetType()))
        {
           
var convertedArg = (TArg)arg;
            caseAction(convertedArg);
        }
        thisCase.NextAction(arg);
    };
   
//no exception by default 
    thisCase.NextAction = (arg) => { };
   
return new Action<object>(thisCase.DoAction);
}

une souscription tel que "Subscribe<MyRectangle>(r => ...), reçoit les messages de Type MyRectangle, mais aussi pour tous les types qui dérivent de MyRectangle.

ainsi si on dérive de MySquare : MyRectangle

class MySquare : MyRectangle
{
}

...
new MySquare() { Width = 4, Ht = 4 }.Publish();
...

   donne comme résultat :
image

avec cette technique il possible de faire des récepteur à messages fortement "typées", certainement moins puissante qu'avec Erlang, mais l'idée est là.

vous pouvez télécharger le code source ici :

 

PDC 08 : Keynote

Le ton est donné ! les prochaines années seront résolument "services" !

un mot à retenir "Azure", un mot oui, mais aussi une plateforme, et un système d'exploitation

à l'occasion du keynote, Microsoft à annoncé sa nouvelle plateforme "Windows Azure", cette plateforme est un système d'exploitation permettant de hoster des applications comme des services, mais en fournissant un abstraction complète vis à vis du système sous-jacent.

Cette plateforme simplifie le déploiement, l'administration et même le développement de vos applications (as services), en s' intégrant dans vos outils tel que Visual Studio.

Au niveau du développement, une nouvelle brique fait son apparition ".NET services". c'est le coeur du système qui permet de connecter les applications entre elles.

The Cloud Computing and Services Platform Diagram

au passage on remarque, les nouveaux logos associés à cette plateforme

pour en savoir plus rendez vous sur http://www.microsoft.com/azure/whatisazure.mspx#Whatis

 

bon je vais suivre la suite sur : "Dublin" and  .Net Services

 

PS : Ray Ozzie à annoncé qu' une première CTP du SDK devrait être bientôt disponible (quelques semaines)

PDC08, un inscrit de plus...

c' est fait, je me suis inscrit, afin d' éviter la bousculade de demain :

image

et vu le temps, un petite balade s' imposait...

image

plage de santa monica, début de "Venice beach"

 

image

demain devrait-être un peu plus climatisé...

 

Mots clés Technorati :
PFX.....

avec la recrudescence des machines multi-processeurs, paralléliser  ses applications devient incontournable.

et pour mieux comprendre les enjeux je vous propose quelques liens.

article de Joe Duffy : http://msdn.microsoft.com/fr-fr/magazine/cc817398.aspx

article de Chance Coble (F#) : http://msdn.microsoft.com/fr-fr/magazine/cc967279.aspx

Stephen Toub et Hazim Shafi : http://msdn.microsoft.com/fr-fr/magazine/cc817396.aspx

une série de vidéos :

http://channel9.msdn.com/posts/JasonOlson/Back-To-Basics-Race-Conditions-Part-1/

http://channel9.msdn.com/posts/Charles/Rahul-Patil-Complexities-of-Testing-Concurrent-Execution/

http://channel9.msdn.com/posts/Charles/Parallel-Computing-Platform-Overview-and-Future-Directions/

http://channel9.msdn.com/posts/Charles/Parallel-Computing-Platform-Asynchronous-Agents-for-Native-Code/

F# nouvelle CTP 1.9.6.2 (update)

Don Syme vient d'annoncer la sortie d' une nouvelle CTP de F#. C'est en réalité une mise à jour, corrections sur les unités de mesure. Cette version est téléchargeable  ici.

Le détails des corrections sont décrites ici.

F# nouvelle CTP (1.9.6)

Que dire de cette CTP, si ce n' est qu' elle est mieux intégrée dans Visual Studio.

image

intellisense

image

de plus intégrable dans une usine de dev, puisque le projet est un projet MSBuild.

 

Mais LA fonctionnalité de cette CTP, c'est les unités de mesures.

#light

//heure
[<Measure>] type h
//km
[<Measure>] type km

j'ai défini un type unité 'h' pour les heures, et un type unité km pour les kilomètres,  je peux maintenant écrire ceci :

let Paris_Marseille = 780.0<km>
let VMax_autorise = 130.0<km/h>

ensuite il suffit d'utiliser ces unités de mesures

image

nbKm est de type <km>. ces unité de mesures sont très bien pour éviter de mélanger les choux et les carottes. Il est possible de définir des fonctions fortement typées, les appels seront contrôlés à la compilation.

let ToKm_h(x) = x * 1.0<km/h> 

let VFR_Max = ToKm_h(240.0)

let ParisMarseille (speed:float<km/h>) = 
  printfn
"Paris-Marseille à %.2f km/h se fait en %.2f heure(s)" 
    <| float(speed) 
    <| float(Paris_Marseille / speed) 

ParisMarseille VMax_autorise
ParisMarseille VFR_Max
ParisMarseille 150.0<km>

la dernière ligne empêche toute compilation

 image

on peut aussi s' amuser avec des unités un peu plus proche de nous......

[<Measure>] type annee
[<Measure>]
type homme
[<Measure>]
type ah = annee / homme 

let heure_par_mois = 169.0<h>
let heure_par_an = 11.0 * heure_par_mois

let ToYear(x:float<h>) = (x / heure_par_an) * 1.0<annee>

let me = 1.0<homme>
let SpentTime(x:float<h>) = ToYear(x) / me

printfn
""
printfn "cet exemple a pris %f annee/homme " <| float(SpentTime(1.0<h>))

le fait de spécifier des unités, cela rend le code plus compréhensible.

au fait avec tout ça j' ai perdu combien d'années ??

image

Mais comment est-ce traduit après compilation ? (que nous dit reflector ?)

image

en double tout simplement, ce qui implique que ces unités de mesures ne sont que du sucre syntaxique pour F#,  mais à mon avis très utile. Pour preuve Andrew Kennedy, relate un fait passé en 1999 (125 million de $, perdu par la NASA, pour une erreur d'unité).

 

1.0<homme> / xxxx<km/h>

C# : Vérifications / Performances

Lorsque que l'on écrit une méthode, il est d' usage de vérifier les paramètres...

mais quelle méthode employez vous ?

la méthode simple, le strict minimum  ?

private static void MyMethod1(string a, string b, string c, string d)
{
   
if (a == null)
       
throw new ArgumentNullException(string.Format(Properties.Resources.EX_1, "a"), (Exception)null);
   
if (b == null)
       
throw new ArgumentNullException(string.Format(Properties.Resources.EX_1, "b"), (Exception)null);
   
if (c == null)
       
throw new ArgumentNullException(string.Format(Properties.Resources.EX_1, "c"), (Exception)null);
   
if (d == null)
       
throw new ArgumentNullException(string.Format(Properties.Resources.EX_1, "d"), (Exception)null);
}

Ou une méthode plus élégante  ? (il y a plus d' informations pour l' appelant lors d' une exception)

private static void MyMethod2(string a, string b, string c, string d)
{
   
ArgumentNotNull(a, Properties.Resources.EX_1, "a");
   
ArgumentNotNull(b, Properties.Resources.EX_1, "b");
   
ArgumentNotNull(c, Properties.Resources.EX_1, "c");
   
ArgumentNotNull(d, Properties.Resources.EX_1, "d");
}

Check.ArgumentNoNull vaut :

public static void ArgumentNotNull(object obj, string fmt, params object[] args)
{
   
if (obj == null)
       
throw new ArgumentNullException(string.Format(fmt, args), (Exception)null);
}

Certes plus élégante, mais elle est malheureusement plus lente à l' exécution. Au premier coup d' oeil, on s' aperçoit qu' à chaque appel, "Properties.Resources.EX_1" est évaluée !

Comparons tout de même :

int nb = 10000;
TimeSpan t1 = DoTest(MyMethod1, nb);
TimeSpan t2 = DoTest(MyMethod2, nb);
Console.WriteLine("t1 : {0}, t2:{1}, t2/t1:{2}", t1, t2, t2.TotalMilliseconds / t1.TotalMilliseconds);

Résultat :

image 

Les chiffres sont petits, mais le nombres d'appels n' est que de 10 000. Le rapport est accablant la deuxième méthode est 86 fois plus lente !!!!!

l' idée ici est gardé un code élégant, tout en se rapprochant des performances de  'MyMethod1'.

Comment évaluer "Properties.Resources.EX_1" uniquement lorsque l' argument est null ?

il suffit de déléguer....

private static void MyMethod2_1(string a, string b, string c, string d)
{
    ArgumentNotNull(a, () => ThrowNull(Properties.
Resources.EX_1, "a"));
    ArgumentNotNull(b, () => ThrowNull(Properties.
Resources.EX_1, "b"));
    ArgumentNotNull(c, () => ThrowNull(Properties.
Resources.EX_1, "c"));
    ArgumentNotNull(d, () => ThrowNull(Properties.
Resources.EX_1, "d"));
}

public static void ThrowNull(string fmt, params object[] args)
{
   
throw new ArgumentNullException(string.Format(fmt, args), (Exception)null);
}

public static void ArgumentNotNull(object obj, Action OnNullAction)
{
   
if (obj == null)
        OnNullAction();
}

et en utilisant les méthodes d' extensions on arrive à quelque chose du genre

private static void MyMethod3(string a, string b, string c, string d)
{
    a.IsNull(() => ThrowNull(Properties.
Resources.EX_1, "a"));
    b.IsNull(() => ThrowNull(Properties.
Resources.EX_1, "b"));
    c.IsNull(() => ThrowNull(Properties.
Resources.EX_1, "c"));
    d.IsNull(() => ThrowNull(Properties.
Resources.EX_1, "d"));
}

et IsNull ressemble beaucoup à ArgumentNotNull

public static void IsNull(this object value, Action OnNullAction)
{
   
if (value == null)
        OnNullAction();
}

on pourrait encore enlever ThrowNull

private static void MyMethod4(string a, string b, string c, string d)
{
    a.IsNull(() => Properties.
Resources.EX_1, "a");
    b.IsNull(() => Properties.
Resources.EX_1, "b");
    c.IsNull(() => Properties.
Resources.EX_1, "c");
    d.IsNull(() => Properties.
Resources.EX_1, "d");
}

 

et les performances ???

image 

en conclusion pour 1 000 000 d' appels, la méthode la moins coûteuse dans les plus élégantes, est la méthode 3

private static void MyMethod3(string a, string b, string c, string d)
{
    a.IsNull(() => ThrowNull(Properties.
Resources.EX_1, "a"));
    b.IsNull(() => ThrowNull(Properties.
Resources.EX_1, "b"));
    c.IsNull(() => ThrowNull(Properties.
Resources.EX_1, "c"));
    d.IsNull(() => ThrowNull(Properties.
Resources.EX_1, "d"));
}

1 000 000 d'appels  () :

  1. Methode 1 : 17.7 ms
  2. Méthode 3 : 44.2 ms

Contexte du Test :

  • Portable Core 2 Duo, 2Ghz, 2Go de RAM
  • .Net 3.5 (sans le sp1)
  • Windows Vista sp1
Comment faire un pattern matching en C# comme celui de F#

il y a une fonctionnalité intéressante dans F#, c' est le pattern matching

Exemple:

let rec fib x = 
 
match x with 
    | 1
->
    | 2
->
    | x
-> fib(x - 1) + fib(x - 2)

en C#

static int Fibonnaci(int x)
{
   
switch (x)
    {
       
case 1 :
           
return 1;
       
case 2:
           
return 1;
       
default :
           
return Fibonnaci(x - 1) + Fibonnaci(x - 2);
    }
}

Dans cas c'est relativement simple, il suffit de remplacer "match x", par "switch(x)". Mais prenons un exemple un peu plus "dynamique" :

let (|ParseInt|_|) s =
 
let i = ref 0
 
if Int32.TryParse(s, i) then Some !i
  
else None

let (|ParseFloat|_|) s =
 
let i = ref 0.0
 
if Double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, i) then Some !i
  
else None

let TryParse v =
 
match v with
    | ParseInt i -> printfn " it's an int : %d" i
    | ParseFloat f
-> printfn " it's an double : %f" f
    | _
-> printfn "unrecognized data"

image

ce qui est intérressant ici, c'est la possibilité de passer des paramètres entre la règle ("case") et le traitement de cette règle. Ce qui n'est plus possible avec un switch...

 

Revisitons le switch...

let rec fib x = 
 
match x with 
    | 1
->
    | 2
->
    | x
-> fib(x - 1) + fib(x - 2)

1,2,x sont des prédicats, et l'on pourrait écrire :

Predicate<int> one = x => x == 1;
Predicate<int> two = x => x == 2;

et le traitement associé est :

Func<int, int> onetwoAction = x1 => 1; 

il faut maintenant relier les règles à l'action, et en tant que développeur de la fonction de fibonnaci, j'ai envie d'écrire :

Func<int, int> Case1 = NewCase(one,  onetwoAction );

Func<int, int> Case2 = NewCase(two,  onetwoAction );

Func<int, int> defaultCase = x => Fib(x-1) + Fib(x-2);

Fib étant la fonction englobant Case1, Case2, defaultCase qui ressemble à :

int Fib(int arg)
{
   
if (one(arg))
       
return onetwoAction(arg);
   
else if (two(arg))
       
return onetwoAction(arg);
   
else
        return defaultCase(arg);
}

mais evidemment tout cela c' est de la plomberie.... En regardant de plus près cela ressemble à une chaine de responsabilité (Chain-of-responsibility pattern).

il faut donc une classe qui possède l'action courante, et l'action suivante

class CaseHolderChain<T>
{
   
public T NextAction { get; set; }
    public T Action { get; set; }
}

La fonction 'NewCase' est donc :

public static Func<TArg, TResult> NewCase<TArg, TResult>(Predicate<TArg> test, Func<TArg, TResult> caseAction)
{
   
if (test == null)
       
throw new ArgumentNullException("test");
   
if (caseAction == null)
       
throw new ArgumentNullException("caseAction");

   
var thisCase = new CaseHolderChain<Func<TArg, TResult>>();

    thisCase.Action = arg =>
    {
       
if (test(arg))
           
return caseAction(arg);
       
return thisCase.NextAction(arg);
    };
    thisCase.NextAction = arg =>
    {
       
return CaseException<TResult>(arg);
    };

   
return thisCase.Action;
}

Ainsi la fonction retournée est une sorte de 'switch' avec une seule possibilité, en effet la fonction 'NextAction' est par défaut 'connectée' sur une méthode static soulevant une exception.

Il faut maintenant lier les 'cases' ensemble, c'est à dire remplacer NextAction du premier 'case' par le suivant, et ainsi de suite...

Mais pour cela il faut faire évoluer notre fonction 'NewCase', car il faut pouvoir changer la valeur de la propriété 'NextAction'.

class CaseHolderChainFun<TArg, TResult> : CaseHolderChain<Func<TArg, TResult>>
{
   
public TResult DoAction(TArg arg)
    {
       
return Action(arg);
    }
}
        
public static Func<TArg, TResult> NewCase<TArg, TResult>(Predicate<TArg> test, Func<TArg, TResult> caseAction)
{
   
if (test == null)
       
throw new ArgumentNullException("test");
   
if (caseAction == null)
       
throw new ArgumentNullException("caseAction");

   
var thisCase = new CaseHolderChainFun<TArg, TResult>();

    thisCase.Action = arg =>
    {
       
if (test(arg))
           
return caseAction(arg);
       
return thisCase.NextAction(arg);
    };
    thisCase.NextAction = arg =>
    {
       
return CaseException<TResult>(arg);
    };
   
return new Func<TArg, TResult>(thisCase.DoAction);
}

plutôt que de renvoyer une fonction pointant directement sur 'Action' (qui est une méthode statique), je renvoie une fonction pointant sur une méthode d'instance. Cela permet de retouver le type 'CaseHolderChainFun<>' facilement.

En effet 'Func<TArg, TResult>' est un type Delegate, qui possède une propriété 'Target', qui est différent de null si la fonction pointe sur une méthode d'instance, bingo !

relier plusieurs 'cases' devient un jeu d'enfant, c'est le rôle de la fonction MakeMatch :

static Func<TArg, TResult> MakeMatch<TArg, TResult>(Func<TArg, TResult> defaultCase, params Func<TArg, TResult>[] cases)
{
   
Func<TArg, TResult> Result;
   
if (defaultCase != null)
        Result = arg => defaultCase(arg);
   
else
        Result = arg => CaseException<TResult>(arg);
   
int i = -1;
    cases.Reverse().ForEach((elem) =>
    {
        i++;
       
CaseHolderChainFun<TArg, TResult> holder = elem.Target as CaseHolderChainFun<TArg, TResult>;
       
if (holder == null)
           
throw new Exception(string.Format("{0}eme case is not supported, use NewCase function ", i));
       
if (Result != null)
        {
            holder.NextAction = Result;
        }
        Result = elem;
    });
   
return Result;
}

Ici MakeMatch, réalise l'union de plusieurs règles entre elle, et en ajoutant à la fin de la chaîne l'action par défaut. Ici l'action par défaut est le premier paramètre, ceci afin de pouvoir utiliser 'params' pour la liste des règles. Maintenant la fonction de Fibonnaci ressemble a :

Func<int, int> fibo = null;
fibo = MakeMatch((
int x) => fibo(x-1) + fibo(x-2),
            NewCase((
int x) => x == 1, x => 1),
            NewCase((
int x) => x == 2, x => 1));

Console.WriteLine(fibo(30));

Nous sommes bien loin du switch, mais celui-ci est 'dynamique'... 

après un petit refactoring et 2 méthodes d'extensions plus tard, la fonction de fibonnaci peut s'écrire :

fibo = fibo.Default(x => fibo(x - 1) + fibo(x - 2))
           .Case(x => x == 1, x => 1)
           .Case(x => x == 2, x => 1);

les méthodes d'extensions ressemble à :

private static TResult CaseException<TResult>(object value)
{
   
throw new Exception(string.Format("case not found for {0}", value));
}

public static Func<TArg, TResult> Case<TArg, TResult>(this Func<TArg, TResult> fn, Predicate<TArg> test, Func<TArg, TResult> caseAction)
{
   
var thisCase = new CaseHolderChainFun<TArg, TResult>();

    thisCase.Action = arg =>
    {
       
if (test(arg))
           
return caseAction(arg);
       
return thisCase.NextAction(arg);
    };
   
if (fn != null)
        thisCase.NextAction = fn;
   
else
        thisCase.NextAction = arg =>
        {
           
return CaseException<TResult>(arg);
        };
   
return new Func<TArg, TResult>(thisCase.DoAction);
}

public static Func<TArg, TResult> Default<TArg, TResult>(this Func<TArg, TResult> fn, Func<TArg, TResult> aDefault)
{
   
var thisCase = new CaseHolderChainFun<TArg, TResult>();

   
if (aDefault != null)
        thisCase.Action = aDefault;
   
else
        thisCase.NextAction = arg =>
        {
           
return CaseException<TResult>(arg);
        };
   
return new Func<TArg, TResult>(thisCase.DoAction);
}

avec cette implémentation, il est impératif que '.Default' soit en premier, car il sera exécuté en dernier.

 

Maintenant que nous avons le principe de base, le 'TryParse' F# devrait ressembler à :

Action<object> TryParse = null;

TryParse = TryParse.Case((
string str, ref int v) => Int32.TryParse(str, out v),
                         (str, v) =>
Console.WriteLine(string.Format("it 's an int -> {0}, {1}", str, v)))
                   .Case((
string str, ref double v) => double.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out v),
                         (str, v) =>
Console.WriteLine(string.Format("it 's an double -> {0}, {1}", str, v)));
TryParse(
"32");
TryParse(
"32.56");
TryParse(
new OverflowException());
TryParse(
new Exception());

et après execution
image

il faut remarquer ici, que le paramètre d'entrée est object, ce qui dans certains cas provoquera du boxing/unboxing.

l'implémentation de la méthode d'extension 'Case' est un peu différente.

public static Action<object> Case<TArg, TRef>(this Action<object> fn, Predicate<TArg, TRef> test, Action<TArg, TRef> caseAction)
{
   
var thisCase = new CaseActionHolder<object>();
    thisCase.Action = (arg) =>
    {
       
if (typeof(TArg).IsAssignableFrom(arg.GetType()))
        {
           
var convertedArg = (TArg)arg;
           
var r = default(TRef);
           
if (test(convertedArg, ref r))
            {
                caseAction(convertedArg, r);
               
return;
            }
        }
        thisCase.NextAction(arg);
    };
   
//no exception by default
    thisCase.NextAction = (arg) => { };
   
return new Action<object>(thisCase.DoAction);
}
public static Action<object> Default(this Action<object> fn, Action<object> aDefault)
{
   
var thisCase = new CaseActionHolder<object>();

   
if (aDefault != null)
        thisCase.Action = aDefault;
   
else
        thisCase.Action = arg => { };
   
return new Action<object>(thisCase.DoAction);
}

Conclusion :

Les expressions lambda  et les méthodes d'extension nous autorise à toucher du doigt la programmation fonctionnelle. Mais dans le cas présent, F# vérifie les règles qui ne "match" pas à la compilation, ce qui n'est pas le cas ici. De plus l'écriture est ici encore plus verbeuse que celle de F#.

 

Matthew Podwysocki à implémenté d'autres aspects fonctionnel que l'on retrouve dans F# (ou d'autre langage fonctionnel), tel Fold/ unfold, Map, Map2, ... , son projet se touve sur : http://code.msdn.microsoft.com/FunctionalCSharp

WPF : la gestion des dates (Label, TextBlock)

Le Label et le TextBlock affichent les dates de manières différentes.

Le label utilise la Culture, et pas le Textblock (du moins pas la culture courante)

Exemple :

<TextBlock Text="binding with TextBlock"></TextBlock>
<
TextBlock Margin="10,0,0,0">
    <TextBlock.Text>
        <Binding Source="{x:Static s:DateTime.Now}">
        </Binding>
    </TextBlock.Text>
</
TextBlock>
<
TextBlock Text="Binding with Label"></TextBlock>
<
Label Margin="10,0,0,0" Padding="0">
    <Label.Content>
        <Binding Source="{x:Static s:DateTime.Now}">
        </Binding>
    </Label.Content>
</
Label>
donne :

image

et pourtant ma cullture est :

image 

mais comment fait donc le "TextBlock" pour m' afficher la date dans ce format ?

et bien tout simplement du binding....

en effet la propriété "Text" du TextBlock étant de type "string", le Binding effectue une conversion, alors que pour le Label, la propriété Content étant de type object, le binding n' effectue aucune conversion.

il reste une question, quelle est la culture utilisée pour la conversion ?

1) si la propriété "ConverterCulture" du Binding est affectée c' est cette culture qui est utilisée.

<Binding Source="{x:Static s:DateTime.Now}"
ConverterCulture="{x:Static sg:CultureInfo.CurrentUICulture}">
</
Binding>

2) Sinon c' est la propriété "Language" du l' élément (FrameworkElement.Language) qui est utilisée.

<TextBlock Margin="10,0,0,0" x:Name="tb1" Language="fr-FR">
    <TextBlock.Text>
        <Binding Source="{x:Static s:DateTime.Now}" x:Name="tbbinding">
        </Binding>
    </TextBlock.Text>
</
TextBlock>

image
très bien mais alors que vaut la propriété "Language", lorsque celle-ci n' est pas spécifiée ?

image

et oui "en-us", mon ami Reflector me le confirme...

 image

Oui mais !

il y a encore un truc qui me chiffonne..., le Label n' affiche pas les secondes !!!

Après un essai "DateTime.Now.ToString()" affiche bien les secondes et utilise la culture.

De plus le Label possède un ContentPresenter pour afficher son contenu, qui lui même à comme template par défaut un "TextBlock"

... dans les deux cas j' ai un TextBlock... mais alors pourqoi l' affichage est différent ?

hum hum, le binding ???

Oui encore lui !

le binding utilisé entre le ContentPresenter et son TextBlock interne est un "TemplateBindingExtension", qui lui par défaut cherche un TypeConverter en faisant quelque chose du style :

TypeConverter tc = TypeDescriptor.GetConverter(typeof(DateTime));
if (tc.CanConvertTo(typeof(string)))
{
   
return (string)tc.ConvertTo(DateTime.Now, typeof(string));
}

(graçe au symboles, j'ai pu parcourir le contenu des sources du ContentPresenter....)

et par défaut le DateTimeConverter effectue un .ToString(format.ShortDatePattern + " " format.ShortTimePattern)

 

à suivre...

Plus de Messages Page suivante »


Les 10 derniers blogs postés

- [Refactoring] ReSharper pour Visual Studio 2010 (Preview) par Thomas Jaskula le il y a 4 heures et 31 minutes

- [Refactoring] Analyser vos exceptions avec ReSharper Exceptional par Thomas Jaskula le il y a 5 heures et 45 minutes

- SharePoint 2007 : patterns & practices SharePoint Guidance par Philippe Sentenac [MVP SharePoint] le il y a 19 heures et 24 minutes

- [Visual Studio 2010] Les tests cases c’est bien, mais je vais devoir tout réécrire ? par Etienne Margraff le il y a 20 heures et 21 minutes

- MVP[Gribouillon].AddYear par The Grib's Lair [Sébastien PICAMELOT - MVP SharePoint] le il y a 20 heures et 36 minutes

- Clinique INSIA - Projet de fin d’Etudes (Silverlight 3 MVVM et OutOfBrowser, WCF, TFS) - Part 1 par David REI le 07-02-2009, 23:38

- C’est la crise ? Bah pourquoi cramer du budget pub alors ? par Nix's Blog le 07-02-2009, 15:31

- Soyons MVP ! par TheSaib .NET blog le 07-02-2009, 12:15

- SharePoint : Gestion des Erreurs 6398, 7076 et 6482 par Blog Technique de Romelard Fabrice le 07-02-2009, 11:53

- EF avec WPF par Matthieu MEZIL le 07-02-2009, 10:18