Same types but with different objects. What is the best way to use it?

Asked

Viewed 566 times

7

I had to create a new object but totally the same as the one in the C#system. Since the objects are the same I tried to do:

ObjetoOriginal.Propriedades = ObjetoClone.Propriedades;

Unfortunately it doesn’t work it’s not?

I tried then:

ObjetoOriginal.Propriedades = (ObjetoOriginal)ObjetoClone.Propriedades;

And nothing.

So I had to do something like this:

ObjetoOriginal.Propriedades.A = ObjetoClone.Propriedades.A;

ObjetoOriginal.Propriedades.B = ObjetoClone.Propriedades.B;

What would be the best way to get all the properties at once?

  • Do you have control over this class? You can touch it?

3 answers

5


First point: you should think 10 times before cloning an object. It is usually problematic to do this and almost always not necessary.

If the object was thought to be cloned it will implement the interface IClonable. I I’ve already answered that. And it is recommended to do this only where it is necessary and with a lot of awareness. It is easy to do wrong.

Sounds to me like you’re trying to clone any object. If the object was not thought to be cloned it is almost certain that it will be making a mistake, but it can use reflection to do this without having to copy each member manually. Ideally create a method to do this in a generic way:

static class ObjectUtil<T> {
    public static T Clone(T obj) {
        Type type = obj.GetType();
        var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        var cloned = Activator.CreateInstance(type);
        for (int i = 0; i < fields.Length; i++) {
            fields[i].SetValue(cloned, fields[i].GetValue(obj));
        }
        return (T)cloned;
    }   
}

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

  • Hello, We had to clone the object because of improvements. We are taking everything there is in one project to another. So during execution I need to "recreate" the same object with the new type. It’s something like my scenario. Anyway, the way I did solves this problem. Problem this I think will not happen again. But I will test what you sent. Abs

  • This is a case that may be useful, but do it carefully. Test well.

2

I come here to add an alternative, which may be useful if the class has a circular reference and you have domain over it.

You can decorate it with the attribute [DataContract] and [DataMember] and serialize the binary of the same using the DataContractSerializer

follow the example.:

var serialize = new DataContractSerializer(typeof(MyClass));
var myClone = default(MyClass);
using (var stream = new MemoryStream())
{
    serialize.WriteObject(stream, myObject);
    stream.Position = 0;
    stream.Flush();
    myClone = (MyClass)serialize.ReadObject(stream);
}

For serialization with cyclic references to work, it is necessary to use the propriate IsReference of DataContrac with a value equal to true;

Here is a complete example.:

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;

namespace ConsoleApplication2
{
    [DataContract(IsReference = true)]
    public class Pessoa
    {
        [DataMember]
        public string Nome { get; set; }

        [DataMember]
        public Pessoa Companheiro { get; set; }

        [DataMember]
        public Pessoa Pai { get; set; }

        [DataMember]
        public Pessoa Mae { get; set; }

        [DataMember]
        public List<Pessoa> Filhos { get; set; }

        [DataMember]
        public List<Pessoa> Irmaos { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var pessoa1 = new Pessoa() { Nome = "Pai" };
            var pessoa2 = new Pessoa() { Nome = "Mãe" };
            var pessoa3 = new Pessoa() { Nome = "Filho 1" };
            var pessoa4 = new Pessoa() { Nome = "Filho 2" };
            var pessoa5 = new Pessoa() { Nome = "Filho 3" };

            pessoa1.Companheiro = pessoa2;
            pessoa2.Companheiro = pessoa1;
            pessoa1.Filhos = pessoa2.Filhos = new List<Pessoa> { pessoa3, pessoa4, pessoa5 };

            pessoa3.Pai = pessoa4.Pai = pessoa5.Pai = pessoa1;
            pessoa3.Mae = pessoa4.Mae = pessoa5.Mae = pessoa2;
            pessoa3.Irmaos = new List<Pessoa> { pessoa4, pessoa5 };
            pessoa4.Irmaos = new List<Pessoa> { pessoa3, pessoa5 };
            pessoa5.Irmaos = new List<Pessoa> { pessoa3, pessoa4 };

            var serialize = new DataContractSerializer(typeof(Pessoa));
            var clone = default(Pessoa);
            using (var stream = new MemoryStream())
            {
                serialize.WriteObject(stream, pessoa1);
                stream.Position = 0;
                stream.Flush();
                clone = (Pessoa)serialize.ReadObject(stream);
            }

            clone.Companheiro.Filhos[1].Nome = "Clone do Filho 2";
            Console.WriteLine($"{clone.Companheiro.Filhos[1].Nome}, {pessoa1.Companheiro.Filhos[1].Nome}, {pessoa4.Nome}");
        }
    }
}
  • +1 like ......

2

Implement the class Icloneable in its class that will provide you to clone a copy of the class with the same values, but being distinct objects. Within the method created by the implementation Icloneable calling Memberwiseclone() that returns a copy in a new object.

Code:

public class Propriedades: ICloneable
{
    public Propriedades()
    {

    }
    public int Id { get; set; } = 1;
    public string Name { get; set; } = "Name 1";

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

Use:

Propriedades p = new Propriedades();
Propriedades c = (Propriedades)p.Clone(); //Clone "ICloneable"

Another way is by using reflection (Reflection):

Propriedades propr1 = new Propriedades();
propr1.Id = 2;
propr1.Name = "Nome 2";
Type propType1 = propr1.GetType();

Propriedades propr2 = new Propriedades();
Type propType2 = propr1.GetType();

foreach(PropertyInfo info in propType1.GetProperties())
{
    propType2.GetProperty(info.Name)
        .SetValue(propr2, info.GetValue(propr1));
}

the above code can be simplified with extension methods with a code like this:

public static class Utils
{
    public static T Clone<T>(this T _t)
        where T: class
    {
        T _r = Activator.CreateInstance<T>();
        Type _t1 = _t.GetType();
        Type _r1 = _r.GetType();
        foreach (System.Reflection.PropertyInfo info in _t1.GetProperties())
        {
            _r1.GetProperty(info.Name)
                .SetValue(_r, info.GetValue(_t));
        }
        return _r;
    }
}

and its use is very similar to the first alternative:

Propriedades propr1 = new Propriedades();
propr1.Id = 2;
propr1.Name = "Nome 2";


Propriedades propr2 = propr1.Clone(); // método de extensão

There’s also a package from nuget the Automapper, example:

Propriedades propr1 = new Propriedades();
propr1.Id = 2;
propr1.Name = "Nome 2";


Mapper.Initialize(cfg => { });
Mapper.Configuration.CompileMappings();

Propriedades propr2 = Mapper.Map<Propriedades>(propr1);

Remarks: i would pass the values to the new instances to have no problem with references and values unexpected. I would only use it if it didn’t compromise the code and its instances, yet the simple assignment mode is the best way to implement.

References:

Browser other questions tagged

You are not signed in. Login or sign up in order to post.