2010-05-06 10 views
4

Je suis en train d'optimiser un morceau de code qui clone un objet:plus rapide pour cloner

#region ICloneable 
public object Clone() 
{ 
    MemoryStream buffer = new MemoryStream(); 
    BinaryFormatter formatter = new BinaryFormatter(); 

    formatter.Serialize(buffer, this);  // takes 3.2 seconds 
    buffer.Position = 0; 
    return formatter.Deserialize(buffer); // takes 2.1 seconds 
} 
#endregion 

plutôt standard. Le problème est que l'objet est assez costaud et cela prend 5,4 secondes (selon ANTS Profiler - je suis sûr qu'il y a la surcharge du profileur, mais quand même).

Existe-t-il un moyen meilleur et plus rapide de cloner?

+0

Cela dépend entièrement de ce que vous essayez de cloner. –

+0

Voir aussi http://stackoverflow.com/questions/852064/faster-deep-cloning – nawfal

Répondre

6
  1. Ne pas implémenter ICloneable.

  2. La manière rapide de cloner un objet consiste à créer une nouvelle instance du même type et à copier/cloner tous les champs de l'instance d'origine vers la nouvelle instance. N'essayez pas de trouver une méthode clone "générique" capable de cloner n'importe quel objet de n'importe quelle classe.

Exemple:

class Person 
{ 
    private string firstname; 
    private string lastname; 
    private int age; 

    public Person(string firstname, string lastname, int age) 
    { 
     this.firstname = firstname; 
     this.lastname = lastname; 
     this.age = age; 
    } 

    public Person Clone() 
    { 
     return new Person(this.firstname, this.lastname, this.age); 
    } 
} 
+3

Je suis d'accord. Je voudrais aussi ajouter une option (3), utilisez un type immuable pour que vous n'ayez pas besoin de cloner. – Aaronaught

+0

L'utilisation de [CGbR] (https://github.com/Toxantron/CGbR) vous donne le même résultat sans avoir à écrire le code vous-même. – Toxantron

1

Si je comprends bien, les cours d'eau, même les intérieurs comme celui-ci, sont chers.
Avez-vous essayé de simplement créer un nouvel objet et mettre à jour les champs pertinents pour amener l'objet au même état? J'ai du mal à croire que votre méthode prenne moins de temps.

+0

C'est plutôt la réflexion utilisée pour réaliser la sérialisation et la désérialisation qui est coûteuse. – Guffa

1

C'est une façon assez chère de cloner. L'objet ne passe jamais sur le fil, donc tout le temps de sérialisation est essentiellement gaspillé. Il sera beaucoup plus rapide de faire clone membre. Je réalise que ce n'est pas une solution automagique, mais ce sera le plus rapide.

Quelque chose le long de ces lignes:

class SuperDuperClassWithLotsAndLotsOfProperties { 
    object Clone() { 
    return new SuperDuperClassWithLotsAndLotsOfProperties { 
     Property1 = Property1, 
     Property2 = Property2, 
    } 

    public string Property1 {get;set;} 
    public string Property2 {get;set;} 
    } 
} 
1

Parce que la copie manuelle des champs est la plus rapide que j'ai créé un générateur de code, qui lit votre définition de classe et génère une méthode clone. Tout ce dont vous avez besoin est le CGbR nuget package et une classe partielle qui implémente ICloneable. Le générateur fera le reste.

public partial class Root : ICloneable 
{ 
    public Root(int number) 
    { 
     _number = number; 
    } 
    private int _number; 

    public Partial[] Partials { get; set; } 

    public IList<ulong> Numbers { get; set; } 

    public object Clone() 
    { 
     return Clone(true); 
    } 

    private Root() 
    { 
    } 
} 

public partial class Root 
{ 
    public Root Clone(bool deep) 
    { 
     var copy = new Root(); 
     // All value types can be simply copied 
     copy._number = _number; 
     if (deep) 
     { 
      // In a deep clone the references are cloned 
      var tempPartials = new Partial[Partials.Length]; 
      for (var i = 0; i < Partials.Length; i++) 
      { 
       var value = Partials[i]; 
       value = value.Clone(true); 
       tempPartials[i] = value; 
      } 
      copy.Partials = tempPartials; 
      var tempNumbers = new List<ulong>(Numbers.Count); 
      for (var i = 0; i < Numbers.Count; i++) 
      { 
       var value = Numbers[i]; 
       tempNumbers[i] = value; 
      } 
      copy.Numbers = tempNumbers; 
     } 
     else 
     { 
      // In a shallow clone only references are copied 
      copy.Partials = Partials; 
      copy.Numbers = Numbers; 
     } 
     return copy; 
    } 
} 

Et la classe partielle

public partial class Partial : ICloneable 
{ 
    public short Id { get; set; } 

    public string Name { get; set; } 

    public object Clone() 
    { 
     return Clone(true); 
    } 
} 

public partial class Partial 
{ 
    public Partial Clone(bool deep) 
    { 
     var copy = new Partial(); 
     // All value types can be simply copied 
     copy.Id = Id; 
     copy.Name = Name; 
     return copy; 
    } 
} 
0

Réponse: Il existe de meilleures méthodes pour le clonage.

Réflexion ou Les arbres d'expression sont beaucoup plus rapides alors sérialisation (réflexion est 5 fois plus rapide, les arbres d'expression sont 20x plus rapide).

enter image description here

Si vous utilisez this linked cloning function comme méthode d'extension, chaque code de clonage réduit à

#region ICloneable 
public object Clone() 
{ 
    return this.DeepCopyByExpressionTree(); 
} 
#endregion 

Pour utiliser la méthode d'extension, il suffit d'avoir le fichier DeepCopyByExptressionTrees.cs n'importe où dans votre solution.

Questions connexes