209 votes

Créer une copie d'un objet en C#

Jetez un coup d'œil au code ci-dessous (extrait d'un livre C#) :

public class MyClass 
{
    public int val;
}
public struct myStruct 
{
    public int val;
}
public class Program 
{
    private static void Main(string[] args) 
    {
        MyClass objectA = new MyClass();
        MyClass objectB = objectA;

        objectA.val = 10;
        objectB.val = 20;

        myStruct structA = new myStruct();
        myStruct structB = structA;

        structA.val = 30;
        structB.val = 40;

        Console.WriteLine("objectA.val = {0}", objectA.val);
        Console.WriteLine("objectB.val = {0}", objectB.val);
        Console.WriteLine("structA.val = {0}", structA.val);
        Console.WriteLine("structB.val = {0}", structB.val);

        Console.ReadKey();
    }
}

Je comprends qu'il produit la sortie ci-dessous :

objectA.val = 20
objectB.val = 20
structA.val = 30
structB.val = 40

Les deux dernières lignes de la sortie ne me posent aucun problème, mais les deux premières me disent que objectA y objectB pointent vers le même bloc de mémoire (puisqu'en C#, les objets sont des types de référence).

La question est de savoir comment faire objectB , une copie de objectA afin qu'il pointe vers une zone différente de la mémoire. Je comprends qu'essayer d'assigner leurs membres peut ne pas fonctionner puisque ces membres peuvent aussi être des références. Alors, comment faire pour que objectB une entité complètement différente de objectA ?

1 votes

Ceci pourrait vous aider : stackoverflow.com/questions/129389/

4 votes

String json = Newtonsoft.Json.JsonConvert.SerializeObject(myobject) ; myObjType rCopy = Newtonsoft.Json.JsonConvert.DeserializeObject<myObjType>(json) ;

0 votes

@hal9000 J'ai utilisé la méthode serialize/deserialize. Y a-t-il des inconvénients à cette méthode par rapport aux solutions de clonage ci-dessous ? Il me semble que c'est une solution simple.

174voto

BugFinder Points 7662

Vous pouvez le faire :

class myClass : ICloneable
{
    public String test;
    public object Clone()
    {
        return this.MemberwiseClone();
    }
}

vous pouvez alors faire

myClass a = new myClass();
myClass b = (myClass)a.Clone();

N.B. MemberwiseClone() Crée une copie superficielle de l'objet System.Object.

38 votes

-1 pour ne pas avoir mentionné le clonage superficiel/profond et ses effets. Sans cette information, MemberwiseClone() est délicate.

8 votes

-1 pour ne pas avoir mentionné que ICloneable est obsolète et ne doit pas être utilisé.

152 votes

@Tom : -1 sur votre commentaire (si je peux) pour ne pas avoir donné de lien sur votre affirmation, car ICloneable n'est pas marqué comme Obsolete dans document officiel ). Il s'agit davantage de bonnes pratiques que d'un statut officiel déprécié.

136voto

Henk Holterman Points 153608

Il n'y a pas de moyen intégré. Vous pouvez demander à MyClass d'implémenter la méthode IClonable (mais elle est en quelque sorte dépréciée) ou écrivez simplement votre propre méthode Copy/Clone. Dans les deux cas, vous devrez écrire du code.

Pour les gros objets, on peut envisager la sérialisation + la désérialisation (à travers un MemoryStream), juste pour réutiliser le code existant.

Quelle que soit la méthode choisie, réfléchissez bien à ce que signifie exactement "une copie". Jusqu'où doit-elle aller, y a-t-il des champs d'identification à exclure, etc.

6 votes

Je suis désolé, je sais que c'est un très vieux message, mais pourriez-vous fournir un lien vers la documentation qui dit que ICloneable est obsolète ? J'ai regardé la documentation pour .net 4.5 et ICloneable ne dit rien sur le fait qu'il est déprécié. Si c'est le cas, j'aimerais utiliser autre chose.

14 votes

Peu importe, je l'ai trouvé. Dans le Documentation .NET ils "recommandent que ICloneable ne soit pas implémenté dans les API publiques". Il n'est donc pas déprécié, mais son utilisation n'est pas recommandée.

20 votes

@LeeTaylor Deprecated signifie "exprimer sa désapprobation à l'égard de" et dans le contexte des logiciels, deprecated signifie exprimer sa désapprobation à l'égard de l'utilisation, absolument. Vous ne pouvez pas avoir une API qui est dépréciée lorsqu'elle est publique et pas lorsqu'elle est privée. Une API dépréciée est dépréciée et ne doit pas être utilisée, sauf en cas d'absolue nécessité. Microsoft ne dit pas qu'il ne faut absolument pas utiliser cette API parce qu'elle est obsolète (il fournit généralement une alternative), il dit simplement qu'il faut éviter de l'utiliser dans les API publiques.

55voto

Levi_vc Points 51

La manière la plus simple d'y parvenir est d'écrire un constructeur de copie dans la classe MyClass.

Voici ce qu'il en est :

namespace Example
{
    class MyClass
    {
        public int val;

        public MyClass()
        {
        }

        public MyClass(MyClass other)
        {
            val = other.val;
        }
    }
}

Le second constructeur accepte simplement un paramètre de son propre type (celui que vous voulez copier) et crée un nouvel objet auquel est attribuée la même valeur

class Program
{
    static void Main(string[] args)
    {
        MyClass objectA = new MyClass();
        MyClass objectB = new MyClass(objectA);
        objectA.val = 10;
        objectB.val = 20;
        Console.WriteLine("objectA.val = {0}", objectA.val);
        Console.WriteLine("objectB.val = {0}", objectB.val);
        Console.ReadKey();
    }
}

de la production :

objectA.val = 10

objectB.val = 20

10 votes

C'est une bonne chose pour l'étude. Pas pour la pratique.

0 votes

La deuxième ligne de la sortie devrait être objectB.val = 20 Impossible d'éditer parce que l'édition est trop petite

17 votes

Le problème avec les constructeurs de copie est que si vous ajoutez/supprimez des champs, vous devez également modifier le constructeur de copie. Cela peut devenir un cauchemar en termes de maintenance. En particulier pour les objets comportant de très nombreux champs (plus de 50, par exemple un contrat de données).

13voto

jgemedina Points 340

Il y a déjà une question à ce sujet, vous pourriez peut-être la lire

Clonage profond d'objets

Il n'y a pas de méthode Clone() telle qu'elle existe en Java par exemple, mais vous pouvez inclure un constructeur de copie dans vos clases, c'est une autre bonne approche.

class A
{
  private int attr

  public int Attr
  {
     get { return attr; }
     set { attr = value }
  }

  public A()
  {
  }

  public A(A p)
  {
     this.attr = p.Attr;
  }  
}

Il s'agit par exemple de copier le membre "Attr" lors de la construction du nouvel objet.

1voto

ChrisF Points 74295

Vous devrez faire quelque chose comme :

MyObject objectB = new MyObject();
objectB.val = objectA.val;

c'est-à-dire créer une nouvelle instance de MyObject . Dans ce cas, c'est trivial, mais si votre classe est plus complexe, vous devriez l'encapsuler dans une méthode de copie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X