Class copy in C#

Asked

Viewed 688 times

12

In C++ programming to copy one class, to another, just do the following:

minhaClasse* class_1 = new minhaClasse();
minhaClasse* class_2 = new minhaClasse();
*class_2 = *class_1; // Faz a atribução membro-a-membro da classe

or

minhaClasse class_1; // declara a classe diretamente, e não um ponteiro
minhaClasse class_2;
class_2 = class_1; // Faz a atribuição membro-a-membro da classe

Whereas to copy the reference, from one class to another, we do the following:

minhaClasse* class_1 = new minhaClasse();
minhaClasse* class_2;
class_2 = class_1; // Faz a a cópia da referência da classe 1 para a classe 2

or

minhaClasse class_1; // declara a classe diretamente, e não um ponteiro
minhaClasse* class_2;
class_2 = &class_1; // Faz a a cópia do referência da classe 1 para a classe 2

From what I have researched from C#, you can only copy the reference from one class to the other, and you can’t make the copy member by member implicitly (as in C++).

That’s true?

  • This description uses the term "reference" wrongly applied to the language C++. Where "reference" is read "pointer". In C++ reference has another technical meaning.

2 answers

11


It’s not true exactly. Of course there’s a way, it’s just different.

Ready-made solution

Every object in C# derives from the class Object which has a method called MemberwiseClone() which can only be accessed within the class (is protected). It makes the blank copy of all members of the object, as you wish.

But note that it does not make the deep copy. That is. It copies everything in the object, bit by bit, but does not create new objects for the references of that object. It does with members the same as the last examples of the question. It copies the references to the new object, but they point to the same objects that the original point to.

So in a shallow copy what modifies in this object does not affect the original object, after all they are very different thing. But if you modify the objects referenced within your object these modifications will be reflected in the original object, since the reference is the same in these objects.

If it’s hard to understand, you need to understand difference between types by value and types by reference. And about immutability.

I talk about it in a PHP question. In general it is more or less the same thing.

There is an example making the deep copy on documentation of MemberwiseClone().

Customized solution

Classes that want to be copied need a method Clone(). This is different from C++ that provides a copy constructor whenever the programmer explicitly prevents it. The copy is normal np C++. In C# it is possible.

The cloning method is only available in the classes that implement the interface IClonable.

In it you can create a shallow or deep copy. It is the decision of the programmer to decide how cloning should proceed. Each class must decide whether to do it this way or not, just as in C++, but in C++ if it does not create anything custom, the copy is shallow. In C# if you want to make only the single copy you need to call MemberwiseClone() within the Clone(), nothing more.

When you use the IClonable any operation that requires cloning can receive this object without knowing what it does.

Deep copy

It is common to make the deep copy, but nothing obliges it to be so. The documentation does not determine what the Clone() should do. Can make shallow, deep copy, or a middle ground. It doesn’t even need to make a complete blank copy, it could just be a selection of members, if it makes sense.

If you decide to make the deep copy method Clone() should create new objects referenced by members and copy their contents to the new reference, probably called the method Clone() of each of them. That is, does what the AP did in the first examples of the question.

If your class’s member objects have methods Clone() that make deep copy, goes firing the copy tree. Hope not to have cyclic reference and not end more (has solution, of course).

Of course cloning is not the only option, there are other ways to copy these objects. An option, if available, is just to create a new object by passing the members of the original as arguments in the constructor, or you can pass the original object as argument. There is a lot of class that does this. It is a way to have a copy constructor equal to C++. In this constructor he reads all members and copies their members properly. Of course, those who consume this, need to know that there is this possibility, is not standardized as cloning "official".

Some cloning is very complex and can go far beyond just copying memory.

It is common for people to have an extension method that makes a deep copy and implements the interface method implicitly.

Non-standard

Some objects may have specific methods to make copies in a specific way. Here it goes to each one. You have to see the class documentation. It cannot be used in a standardized way as the MemberwiseClone() and the Clone(). An example is the Array.Copy(). It is also common to implement a specialized version of Clone() returning the class type object. The implementation of IClonable must return a object, which may require a cast in their use.

In theory it is possible to clone even what is not prepared to do so. But it is complicated. You have to resort to reflection to ensure that everything that is needed will be done, you have to access internal things that are implementation details and nothing guarantees that this will always work.

Serialization is an option.

Note that it is not so common to use this interface in C#. It is rare to use copy of the content since it can be expensive and almost always not the desired semantics. Hence the existence of more specific methods when it makes sense. C++ itself has preferred to move rather than copy.

Completion

The theme is extensive, theme enough thing to learn about it, has a lot of problematic situation.

I made an example with two forms of cloning.

Behold working in the ideone. And in the .NET Fiddle. Also put on the Github for future reference.

  • I get it, the method Clone() does not implicitly call the method Clone() objects contained in the class (as does C++ with the map, for example, using the operator overload '=') ? Maybe it’s a stupid question, but I’ve been programming in C++ for many years and I’m a little too used to it.

  • It does if you say it should. What the Clone() must do is the decision of the creator of the class. In the example of the map was decided to do just that, speaking in terms of C#, was given a clone in its members. You don’t see that. The call of Clone() members is made within the Clone() of that object. He called it, it goes off firing all necessary cloning. But there is a case that the creator of the class considers that this is not appropriate. In theory you do whatever you want inside the Clone(). But in almost all cases will call the MemberwiseClone() at some point.

  • @bigown what’s the difference of a shallow copy and a deep one? It confused me a little bit.

  • It is difficult to say this in comment. More or less it is q that is in the 3rd. paragraph that probably did not give to understand. The shallow copy is bit by bit of the object. A deep, it fires copies of the referenced objects. Obviously it only does this with type objector by reference. Type by value the bit-by-bit copy is sufficient. When you have a reference has or another object somewhere, you need to copy it too, if you copy only the value, that is, the reference, you will have two objects with the same reference obviously pointed to the same object. The right would be to have the new object creation. It is the q gave here.

  • @Denercarvalho I put one link in the answer to a question I answered that might help.

  • An alternative, to copy the class is Serializing in memory and after deserialize, of course this will spend a little resources hehehe...

  • Funny how something that should be practical is complicated in languages like C# and Java. It is especially difficult to explain the distinction between a reference and value (in languages in which objects are passed through "copy of reference" the brain of a programmer accustomed to having more control hurts). Then people start to Serialize / Deserialize, write Frameworks to clone objects and things like that. I honestly think that this is a misunderstanding and wrong problem in Java. It seems that C# follows the same line.

  • @Anthonyaccioly C# copied much of the defects of Java, mostly letting the objects be null by default. But actually it is a complicated problem to solve even, can not have single rule, even C++ has problems, just has an extra ease.

  • Fair engough. To do justice I still think that C# hit this and other issues a little better (for example ref and out do not exist in Java), nothing like enter a few years later to make an improved "copy". That said, it’s still scary that both languages have no methods shallowCopy and deepCopy with possible state variants (such as the method copy in Scala. You can change any parameter named during copying).

  • @Anthonyaccioly I think this one of the huge errors of Java, in C# 7 are amplifying the use of ref in a very certain way, and facilitating the use of out, more because it has a legacy, because they are creating tuples in the language, which makes the out obsolete. C# 7 seems to improve on the copy in some situation, but I haven’t gone into it yet. Java 10 solves one of the problems that C# has solved, but without ref He can’t be used that much. I think C# has tidied up a lot, but I still think a new language with what you know today would be useful (and if you abandon the *C-like syntax, even better).

Show 5 more comments

6

For default, C# copies the reference and not the object. But you can use the interface ICloneable to make copies of the object instance:

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

So do:

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

Browser other questions tagged

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