Blog Arolla

Inspiration Fonctionnelle – La méthode d’extension “With”

Ajouter une méthode d’extension “With” imitant F#

Ce billet est le premier d’une série sur la façon dont la programmation fonctionnelle, et la formation F# à laquelle j’ai récemment assisté, m’apportent de l’inspiration dans mon travail quotidien en C#. Ce premier billet montre comment mettre en place une fonctionnalité similaire au “with” de F#.

Une des nombreuses fonctionnalités F#, qui m’a interpelé durant le cours “Débuter en F#” de Robert Pickering, a été le principe des Record Types et leur facilité d’utilisation , par rapport à un Tuple, dont les propriétés ne peuvent pas être nommées.

Par exemple, cet extrait de code F# définit un Record Type “Person” comportant quelques propriétés.

type Person =
    { Name: string;
      Town: string;
      Country: string;
      Age: int }

 

Une fois ce type défini, il est possible d’en créer une instance :

let someone =
    { Name = "Luc";
      Town = "Paris";
      Country = "France";
      Age = 29 }

 

Et une fonctionnalité très pratique du F# permet alors d’écrire :

let someoneElse =
    { someone with
        Age = 30 }

Le mot-clé “with” permet de définir “someoneElse” comme une copie de “someone”, en précisant uniquement les propriétés à changer.

Dans un projet sur lequel j’ai travaillé récemment, j’ai rencontré exactement ce besoin, de définir une instance semblable à une autre, avec très peu de changements, mais cette fois en C#. Afin de réaliser ceci, j’ai écrit une simple méthode d’extension, basée sur l’interface ICloneable :

public static class ClonableExtensions
{
    public static T With<T>(this T original,
                            Action<T> withAction)
        where T : ICloneable
    {
        T copy = (T)original.Clone();
        withAction(copy);
        return copy;
    }
}


Avec cette méthode d’extension, si on définit une classe “Person” implémentant l’interface ICloneable, comme ceci :

public class Person : ICloneable
{
    public string Name { get; set; }
    public string Town { get; set; }
    public string Country { get; set; }
    public int Age { get; set; }

    public object Clone()
    {
        return new Person()
        {
            Name = this.Name,
            Town = this.Town,
            Country = this.Country,
            Age = this.Age
        };
    }
}


Il est maintenant possible d’utiliser la méthode générique With pour écrire le code suivant :

Person someone = new Person()
{
    Name = "Luc",
    Town = "Paris",
    Country = "France",
    Age = 29
};

Person someoneElse = someone.With(p => p.Age = 30);

Je trouve cette construction à la fois expressive et lisible, mais il faut tout de même noter quelques inconvénients :

  1. L’objet C# doit être muable.  En effet, sa propriété “Age” est mise à jour après la création de l’objet par la méthode Clone. Même si la méthode Clone aurait pu utiliser un constructeur de copie plutôt que d’écrire dans les propriétés publiques, la méthode générique With doit pouvoir par la suite écraser les valeurs de ces propriétés (une solution à ce problème sera proposée dans un prochain billet).
  2. La syntaxe à utiliser lorsqu’on souhaite modifier plus d’une propriété est moins lisible, car une expression lambda à plusieurs instructions doit être placée entre accolades.

@pirrmann

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *