[C#] Clonez typé !

Lorsque l’on clone un objet sous .NET qui implémente l’interface ICloneable, on doit toujours « caster » le résultat de la méthode « Clone ».

Voici un exemple avec l’objet System.Drawing.Font :

Font f;
Font objetCloné;

f = new Font("Arial", 16);
objetCloné = (Font)f.Clone();

La méthode Clone de l’interface ICloneable implémenté par Font renvoi bien évidemment un objet de type Font mais typé « object ». Un cast sera donc forcement nécessaire pour pouvoir compiler sans erreurs. (C’est le cas dans le code précédent).

Seulement il se peut un jour que par erreur (surtout avec le Refactoring) vous changez le type d’objet de la variable « objetCloné » pour que le code se compile sans problème, mais lève une exception à l’exécution !

Font f;
Color objetCloné;

f = new Font("Arial", 16);
objetCloné = (Color)f.Clone();

Un tel code compile sans problème, mais provoque la levée d’une exception avec un message d’insulte à l’exécution.

Un beau message d'insulte de la part du CLR...

Pour éviter ce genre d’étourderie, on peut utiliser avec le .NET Framework 2.0 les Generics afin de créer une méthode générique qui s’occupe de réaliser le clonage et de renvoyer un objet typé :

public static class Outils
{
  public static T Cloner(T objet) where T : ICloneable
  {
    return (T)objet.Clone();
  }
}

Il suffit maintenant d’appeler cette méthode statique comme ceci :

Font f;
Font objetCloné;

f = new Font("Arial", 16);
objetCloné = Outils.Cloner(f);

Maintenant essayez de changer le type de l’objet cloné.
A la compilation Visual Studio vous générera une exception vous indiquant qu’il est impossible de convertir un Font en un autre objet.

Erreur détectée à la compilation

Cette petite astuce vous permet de typer fortement votre code et ainsi d’éviter de contrôler du code à l’exécution ! Notez au passage que vous pouvez cloner un objet uniquement si celui-ci implémente la méthode ICloneable.

Gardez à l’esprit qu’un code très typé permet de détecter un maximum d’erreur de casting « bête » à la compilation !

A la question des débutants qui vont me spammer à la fin de la lecture de cette article (et qui ont déjà commencé !) :« Mais cela ne fonctionne pas si l’objet renvoyé n’est pas de même type ».
Je leur répondrais : « Dans ce cas si vous partez de ce principe revoyez votre code immédiatement ! »
En effet, si vous clonez une maison et que vous obtenez un panda… Cela dénote très certainement une erreur de compréhension concernant la définition du mot « clonage » !

Bonus de cet article :

Un petit bonus pratique pour ceux qui lisent cet article jusqu’au bout…
Modifier le contenu de la méthode comme ceci :

public static T Cloner<T>(T objet) where T : ICloneable
{
  if (objet == null)
    return null;

  return (T)objet.Clone();
}

Ainsi vous pouvez appelez la méthode de clonage même si l’objet à cloner n’existe pas ! Cette petite méthode est fort utile lorsque l’on clone une série d’objets imbriqués comme ceci :

public object Clone()
{
  MonObjet o;
  o = this.MemberwhiseClone();

  o.variableMembre1 = Outils.Cloner(this.variableMembre1);
  o.variableMembre2 = Outils.Cloner(this.variableMembre2);

  return o;
}

Grâce à la précédente méthode, il est inutile de tester chaque variable membre à cloner si elle ne sont pas définie à null.