Dans cet article, je vais vous expliquer comment réussir à cloner des objets C# en profondeur de manière simple et surtout efficace.
L’idée est d’écrire du code qui permettra de cloner des objets même si les classes évoluent.
Qu’est ce que le clonage d’objects ?
Cloner des objets veut simplement dire créer une nouvelle instance et copier toutes les propriétés d’un objet « source ». Si l’objet « source » contient des sous-objets, il faut aussi les copier (en profondeur).
Voici un exemple si on devait le faire « à la main »:
[csharp]class Utilisateur
{
public string Nom { get; set; }
public string Prenom { get; set; }
}
class Commande
{
public Utilisateur Utilisateur { get; set; }
public string Produit { get; set; }
}
// dans main…
class Program
{
static void Main()
{
Commande c1 = new Commande();
c1.Produit = "Voiture";
c1.Utilisateur = new Utilisateur()
{
Nom = "Smith",
Prenom = "John"
};
// Faire la copie (clonage)
Commande c2 = new Commande();
c2.Produit = c1.Produit;
c2.Utilisateur = new Utilisateur();
c2.Utilisateur.Nom = c1.Utilisateur.Nom;
c2.Utilisateur.Prenom = c1.Utilisateur.Prenom;
}
}
[/csharp]
Vous aurez compris le principe.
Cette méthode est simple mais elle comporte un gros problème : à chaque fois que les classes Commande ou Utilisateur évoluent, il faut mettre à jour le code qui effectue le clonage. Ce type de comportement ne respecte pas le principe OCP et va donc alourdir la maintenance.
La solution pratique à utiliser
Pour corriger cela, je vous propose une astuce simple pour faire du clonage d’objets sérialisables (il faudra ajouter l’attribut [Serializable] sur vos classes).
Elle utilise les mécanismes de serialisation/déserialisation pour construire un nouvel objet copié.
L’astuce provient à l’origine du site CodeProject (consulter l’article sur le site CodeProject).
Voici le code de la méthode de copie :
[csharp] public static T Clone<T>(T source){
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("The type must be serializable.", "source");
}
// Don’t serialize a null object, simply return the default for that object
if (Object.ReferenceEquals(source, null))
{
return default(T);
}
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
[/csharp]
Ensuite, pour l’utiliser, il suffit d’appeler la méthode comme ceci :
[csharp] Commande c3 = ObjectCopier.Clone(c1);[/csharp]
Cette technique est très puissante et facile à mettre en oeuvre. Elle vous permet aussi de faire du clonage sans avoir besoin d’écrire le code pour copier chaque propriété. De plus, elle permet de s’adapter automatiquement aux évolutions des classes (principe OCP).
Si vous souhaitez mettre en oeuvre cette technique, je vous ai préparé un projet de démonstration que vous pouvez télécharger juste en dessous.
[sociallocker] Télécharger le projet de démonstration pour le clonage d’objets[/sociallocker]
Bonjour,
On m’a demandé ce qu’il en était des performances ?
Avez vous eu l’occasion d’implémenter cette solution ?
Merci
Bonjour,
Evidemment, c’est souple et automatique mais ça n’est pas aussi rapide « qu’à la main ».
Petite comparaison, pour 1 000 000 copies :
– clonage à la main (en copiant toutes les propriétés) : 0,16 s
– clonage automatique : 13,65 s
Il y a toute de même un facteur 85 entre les deux. Il faut donc faire un choix entre le côté pratique et les performances.
S’il y a beaucoup de copies à faire, il faudra optimiser et écrire le code complet.
C’est aussi la première chose qui m’est venue à l’esprit : les performances !
Je me suis posé une autre question existentielle l’autre jour, ne pourrait-on pas développer une sorte de « plugin » pour Visual Studio qui permette de paramétrer les classes POCO de manière visuelle, un peu façon diagramme de classe, et qui génère ensuite le code source de ces classes automatiquement, c’est à dire les propriétés, les méthodes de clonage, mais aussi des événements pour faciliter les développements MVVM comme les applications WPF ?
Salut Stéphane,
Et bien pour générer du code, il y a déjà les templates T4 (extension .tt) que l’on peut utiliser. Ce n’est pas visuel mais ça permet de créer des classes POCO avec éventuellement des méthodes de clonage.
Entity Framework utilise ce mécanisme.
Pour faciliter l’utilisateur, il faudrait écrire un petit fichier de config pour éviter de modifier toute le temps le template T4.
As-tu déjà essayé le T4 ?