Accueil > CSharp > Using using…

Using using…

Mois d’aout, vacances, coding, sport, mais aussi Windows 10, VS 2015 et aussi un peu de temps pour creuser quelques idées testées dans l’année.

IMG_4479

Ici une idée très simple pour sécuriser un code encore plus simple mais qui nous embête régulièrement.

Il y a quelque années (oups 8 ans…) j’avais profité des méthodes anonymes de C# pour tester un moyen de factoriser du code “englobant” tel qu’avec les instructions try…catch.

Vous pouvez encore trouver ici mon billet relatif de l’époque.

On peut appliquer cette solution à tout type de code ayant une logique d’ouverture puis de fermeture: try…catch, début et fin de transaction, début et fin de mesure de performance, etc…

En plus de simplifier le code, ce système le sécurise également car il est vital que le code de fermeture soit appelé (Commit/Rollback, stopWatch.Stop, etc).

Par contre, on peut regretter la syntaxe toujours un peu spéciale des méthodes anonymes ou des expressions lambdas qui apportent un peu de complexité. Pour rappel, un objet est spécialement créé pour porter le contexte d’exécution d’une méthode anonyme, bref on aurait préféré l’utilisation d’un bloc de code classique et c’est tout le propos de ce billet !

Commençons par faire quelques rappels sur le mot clé “using”.

“Using” est très apprécié des développeurs C#, justement pour sa simplicité d’utilisation. Ce mot clé est intimement rattaché à l’interface IDisposable. C’est un des très rares cas où le langage est lié à un élément spécifique du framework.

class Program
{
    static void Main(string[] args)
    {
        using (var t = new Test())
        {
            Console.ReadLine();
        }
        Console.ReadLine();
    }
}
class Test : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("Dispose!");
    }
}

Dans l’exemple ci-dessus, l’instruction “using” assure l’appel de la méthode Dispose après l’exécution de l’instruction (ou du bloc d’instruction) suivant.

Si l’on désassemble l’IL compilé, on voit que le compilateur génère un bloc try…finally pour sécuriser l’appel du Dispose

image

On notera également que IDisposable, même si son nom pourrait le faire croire, n’est lié à aucun mécanisme de libération de mémoire. On l’utilise en général pour assurer une libération de ressource logique. Nous avons donc tout le loisir de l’utiliser pour tout autre besoin.

On peut donc facilement développer quelques classes utilisant IDisposable afin de simplifier et sécuriser l’utilisation d’un simple verrou boolean (true au démarrage, puis false en sorte) ou encore d’une mesure de temps d’exécution comme le montre les exemples ci-dessous:

public class SafeBooleanLock : IDisposable
{
    public bool Lock { get; private set; }
    public SafeBooleanLock()
    {
        Lock = true;
    }
    public void Dispose()
    {
        Lock = false;
    }
}
public class LogExecutionTime : IDisposable
{
    private Stopwatch sw;
    public LogExecutionTime()
    {
        sw = Stopwatch.StartNew();
    }
    public void Dispose()
    {
        sw.Stop();
        Debug.WriteLine(sw.ElapsedMilliseconds);
    }
}

Tout le gain est bien évidemment mesuré côté appelant, le code est factorisé, sécurisé et ce avec un simple bloc de code (sans lambda).

using (new LogExecutionTime())
{
    //Some code...
    Console.ReadLine();
}

Très bonnes vacances et très bonne lecture à tous !

Mitsu

Catégories :CSharp Étiquettes : ,
  1. 02/08/2015 à 20:05

    Je n’ai jamais pensé a utiliser les using comme ça c’est une très bonne idée 🙂

  2. 03/08/2015 à 03:09

    Exemple de technique similaire dans Orchard, pour déclarer un bloc de script qui devra être rendu au bas du body:

    using (Script.Foot) { … }

    Le « using » permet la capture du markup inclus dans le bloc. C’est magique…

  3. 03/08/2015 à 13:06

    Good. J’ai déjà utilisé ce style de mécanisme et j’aimerais partager mon retour. Cela consolide beaucoup l’encapsulation des traitements et permet de ne pas exposer publiquement certaines méthodes qui peuvent rester « private ». Par contre, du côté de l’appelant, on a une dissimulation de code qui est un peu dérangeante, puisque certaines choses se produisent dans le « fin accolade » au niveau de l’IDE. De plus, dans le code que j’ai pu écrire, je me suis beaucoup demandé si ça n’incitait pas à conférer plusieurs responsabilités à la classe (je m’en servais pour dumper certaines choses en fin de traitements, logger, etc…). Depuis, pour faire ce style de choses, je préfère utiliser un « ambiant context » qui est plus parlant sur ses intentions (+ de code, mais + clair, d’après moi).

  4. 03/08/2015 à 17:45

    Merci pour le partage d’expérience que je comprends tout à fait. Je pense que cela dépend beaucoup du contexte. Si c’est fonctionnel, en effet on perd en lecture et on peut oublier ce qu’il se passe réellement, mais de toute façon il faut factoriser le code quand même. Si c’est technique, c’est intéressant de sécuriser tout en masquant la complexité, comme on le fait de toute manière avec des composants, de l’héritage, etc.

  5. girafologue
    03/08/2015 à 23:34

    Le seul truc vraiment dommage, c’est qu’il y ait cette interface et non une convention comme avec await. Du coup, même si tu implémente idisposable dans une structure, au moment ou dispose est appelé, ton objet est boxé (donc allocation dynamique, pressure gc etc.). Du coup, ca ne convient pas pour du code haute perf (genre un mutex ou Reader writer lock). A priori ca me semble être d’ailleurs une des raisons de la présence du mot-clef lock.

  1. No trackbacks yet.

Répondre à Rénald VENANT-VALERY Annuler la réponse.