web-dev-qa-db-fra.com

Comment cloner des objets

Quand je fais ce qui suit ... tout ce qui est fait à la personne b modifie la personne a (je pensais que cela clonerait la personne b de la personne a). Je ne sais pas non plus si changer de personne a changera de personne b après la liaison. En raison de mon code actuel, je ne peux le voir que dans 1 direction.

Person a = new Person() { head = "big", feet = "small" };
Person b = a; 

b.head = "small"; //now a.head = "small" too   

Maintenant, si je le fais à la place .. La personne a devient complètement séparée.

Person b = new Person() { head = a.head, feet = a.feet };

Maintenant, cette amende et un peu logique a du sens quand on compare ce comportement à d’autres choses en C #. MAIS, cela pourrait devenir très ennuyeux avec de gros objets.

Y at-il un moyen de raccourcir cela du tout?

Tel que:

Person b = a.Values;

50
PiZzL3

Y at-il un moyen de raccourcir cela du tout?

Non, pas vraiment. Vous devrez créer une nouvelle instance afin d'éviter que l'original n'affecte la "copie". Il y a plusieurs options pour cela:

  1. Si votre type est un struct, pas un class, il sera copié par valeur (au lieu de simplement copier la référence à l'instance). Cela lui donnera la sémantique que vous décrivez, mais a de nombreux autres effets secondaires qui ont tendance à être moins qu'attirés, et n'est pas recommandé pour tout type mutable (ce qui est évidemment, ou ce ne serait pas un problème!)

  2. Implémentez un mécanisme de "clonage" sur vos types. Cela peut être ICloneable ou même simplement un constructeur qui prend une instance et en copie des valeurs.

  3. Utilisez réflexion, MemberwiseClone ou similaire pour copier toutes les valeurs, vous n'avez donc pas besoin d'écrire le code pour le faire. Cela peut poser des problèmes, en particulier si vous avez des champs contenant des types non simples.

39
Reed Copsey

Ce que vous cherchez, c'est un clonage. Vous aurez besoin de mettre en œuvre IClonable et ensuite faire le clonage.

Exemple:

class Person() : ICloneable
{
    public string head;
    public string feet; 

    #region ICloneable Members

    public object Clone()
    {
        return this.MemberwiseClone();
    }

    #endregion
}

Ensuite, vous pouvez simplement appeler la méthode Clone pour faire un ShallowCopy (Dans ce cas particulier également un DeepCopy)

Person a = new Person() { head = "big", feet = "small" };
Person b = (Person) a.Clone();  

Vous pouvez utiliser la méthode MemberwiseClone de la classe Object pour effectuer le clonage.

63
Shekhar_Pro

J'utilise AutoMapper pour cela. Cela fonctionne comme ceci:

Mapper.CreateMap(typeof(Person), typeof(Person));
Mapper.Map(a, b);

Maintenant, la personne a a toutes les propriétés de la personne b.

De plus, AutoMapper fonctionne également pour différents objets. Pour plus d'informations, consultez la page http://automapper.org

Mise à jour: j'utilise cette syntaxe maintenant (de manière simpliste - les CreateMaps sont réellement dans des profils AutoMapper):

Mapper.CreateMap<Person, Person>;
Mapper.Map(a, b);

Notez qu'il n'est pas nécessaire de créer une carte CreateMap pour mapper un objet du même type sur un autre, mais dans le cas contraire, AutoMapper créera une copie superficielle, ce qui signifie pour le profane que si vous modifiez un objet, l'autre les changements aussi.

14
Kasey Krehbiel

La méthode MemberwiseClone () n'étant pas publique, j'ai créé cette méthode d'extension simple afin de faciliter la copie d'objets:

public static T Clone<T>(this T obj)
{
    var inst = obj.GetType().GetMethod("MemberwiseClone", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

    return (T)inst?.Invoke(obj, null);
}

Usage:

var clone = myObject.Clone();
13
tocqueville

Pour cloner votre objet de classe, vous pouvez utiliser la méthode Object.MemberwiseClone,

ajoutez simplement cette fonction à votre classe:

public class yourClass
{
    // ...
    // ...

    public yourClass DeepCopy()
    {
        yourClass othercopy = (yourClass)this.MemberwiseClone();
        return othercopy;
    }
}

puis pour effectuer une copie indépendante profonde, appelez simplement la méthode DeepCopy:

yourClass newLine = oldLine.DeepCopy();
8
Chtiwi Malek

a et b ne sont que deux références au même objet Personne. Ils détiennent tous deux essentiellement l'adresse du Person.

Il existe une interface ICloneable , bien que relativement peu de classes le prennent en charge. Avec ceci, vous écririez:

Person b = a.Clone();

Alors, b serait un tout à fait séparé Person.

Vous pouvez également implémenter un constructeur de copie:

public Person(Person src)
{
  // ... 
}

Il n'y a pas de moyen intégré pour copier tous les champs. Vous pouvez le faire par réflexion, mais il y aurait une pénalité de performance.

6
Matthew Flaschen

Sans peine: Utilisation de la bibliothèque NClone

Person a = new Person() { head = "big", feet = "small" };
Person b = Clone.ObjectGraph(a); 
2
magallanes
  public static T Clone<T>(T obj)
  {
      DataContractSerializer dcSer = new  DataContractSerializer(obj.GetType());
      MemoryStream memoryStream = new MemoryStream();

      dcSer.WriteObject(memoryStream, obj);
      memoryStream.Position = 0;

      T newObject = (T)dcSer.ReadObject(memoryStream);
      return newObject;
  }
1
Tatinfo

MemberwiseClone est un bon moyen de faire une copie superficielle, comme d'autres l'ont suggéré. Cependant, il est protégé, donc si vous voulez l'utiliser sans changer de classe, vous devez y accéder par réflexion. La réflexion est cependant lente. Donc, si vous envisagez de cloner beaucoup d'objets, il peut être intéressant de mettre en cache le résultat:

public static class CloneUtil<T>
{
    private static readonly Func<T, object> clone;

    static CloneUtil()
    {
        var cloneMethod = typeof(T).GetMethod("MemberwiseClone", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
        clone = (Func<T, object>)cloneMethod.CreateDelegate(typeof(Func<T, object>));
    }

    public static T ShallowClone(T obj) => (T)clone(obj);
}

public static class CloneUtil
{
    public static T ShallowClone<T>(this T obj) => CloneUtil<T>.ShallowClone(obj);
}

Vous pouvez l'appeler comme ça:

Person b = a.ShallowClone();
0
Tim Pohlmann

Vous pouvez le faire comme ceci:

var jss = new JavaScriptSerializer();
var b = jss.Deserialize<Person>(jss.Serialize(a));

Pour le clonage en profondeur, vous voudrez peut-être jeter un coup d'oeil à cette réponse: https://stackoverflow.com/a/78612/550975

0
Serj Sagan

À mon avis, la meilleure façon de procéder consiste à implémenter votre propre méthode Clone() comme indiqué ci-dessous.

class Person
{
    public string head;
    public string feet;

    // Downside: It has to be manually implemented for every class
    public Person Clone()
    {
        return new Person() { head = this.head, feet = this.feet };
    }
}

class Program
{
    public static void Main(string[] args)
    {
        Person a = new Person() { head = "bigAF", feet = "smol" };
        Person b = a.Clone();

        b.head = "notEvenThatBigTBH";

        Console.WriteLine($"{a.head}, {a.feet}");
        Console.WriteLine($"{b.head}, {b.feet}");
    }
}

Sortie:

bigAf, smol

notEvenThatBigTBH, smol

b est totalement indépendant de a, car ce n’est pas une référence, mais un clone.

J'espère pouvoir vous aider!

0
Wahoo