3

Suppose we have the C# code below that stores an instance of class A in class B. I want to create a version of class B that stores its own A. So when we change A outside of B, the internal A object in B doesn't change.

class Engine
{
    public int Horesepower = 500;
}
class Vehicle
{
    public Vehicle( Engine engine ) { Engine = engine; }
    public Engine Engine;
}

class Program
{
    static void Main( string[ ] args )
    {
        Engine v8 = new Engine( );
        Vehicle monsterTruck = new Vehicle( v8 );
        v8.Horesepower = 1000; // this changes the field Engine in yugo as well

        Console.WriteLine( monsterTruck.Engine.Horesepower ); // prints 1000 instead of 500
    }
}

I can avoid this problem in C++ by using a templated bridge class that clones A when passing A into B. But apparently deep clones doesn't seem as widely used in C# for some reason(why?). Can anyone provide a good way around this storing problem.

C++ code:

template <typename T>
class Wrapper{
public: 
    Wrapper(const Wrapper<T> &orig){if(orig.data!=nullptr)data = orig.data->clone();}
    Wrapper(const T &origdata){data = origdata.clone();}
    Wrapper & operator=(const Wrapper<T> &orig){...}
    ... 
    ~T(){delete data;}
private:
    T *data;
}

class A{
public: 
    A():data(9){}
    int data;
    A *clone(){return new A(*this);}
}

class B{
public: 
    B(const Wrapper<A>& a_):ainner(a_){}
    Wrapper<A> ainner;
}

int main(){
    A a;
    B b(a);
    a.data =20;
    std::cout << b.ainner.data; // prints 9 still
    return 0;
}
8
  • 2
    You can implement a deep clone yourself. The ICloneable interface exists for that reason. (Not the best interface design but that's what it is.) msdn.microsoft.com/en-us/library/… Commented Dec 31, 2014 at 16:06
  • 1
    From what I know, IClonable doesn't differentiate between shallow and deep clones. Commented Dec 31, 2014 at 16:07
  • Clone() is generally used for deep cloning. Object.MemberwiseClone performs shallow copies of objects already. You're right that the interface doesn't specifically mentions it but if you read the documentation, it discusses deep clones. As I said, it's not a well designed interface. Commented Dec 31, 2014 at 16:08
  • Clone doesn't perform deep copies on ref type properties.. Commented Dec 31, 2014 at 16:12
  • ICloneable.Clone doesn't do anything. It depends on the implementation. Commented Dec 31, 2014 at 16:23

3 Answers 3

3

Using Clone() will only satisfy your needs if the object you are cloning contains value types, but typically (as in your example) you would have a class with other reference types properties and a shallow copy ( via Clone() ) will simply copy a reference.

What you want is a deep copy and that can be done either by implementing copy constructors on all your properties that are ref types or thru Serialization.

Serialization would be the simplest way to perform a deep copy: Deep cloning objects

Copy constructors doing deep copy example:

class Engine
{
    public Engine( ) { }
    public Engine( Engine e ) { Horesepower = e.Horesepower; }

    public int Horesepower = 500;
}
class Vehicle : ICloneable
{
    public Vehicle( Engine engine ) { Engine = engine; }
    public Vehicle( Vehicle v ) { Engine = new Engine( v.Engine ); }

    public Engine Engine;

    public object Clone( )
    {
        Vehicle newVehicle = new Vehicle( this );
        return newVehicle;
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

I feel like serialization seems to be a bit excessive for my problem. Coming from a C++ background, is deep cloning something that is not really necessary in C# like passing constants parameters? I don't want to write non-idiomatic code.
it is used quite a lot, if you don't want to do serialization then you need to write a copy constructor.
Serialization is a very heavy hammer for cloning. Should be a last resort, if you have control over all the code.
I think I might have to use serialization in the end because copy constructors probably won't work for derived classes. It will end up copying only the base class.
if you have access to all code/classes then either will work, in the end serialization will adapt easier to mods but copy constructors are more efficient.. in the end it's up to you.
|
2

Remember that you're passing references around. If you pass your instantiated copy of A into something, you're passing THAT copy of A into it (as if you had passed &a, in C).

Since classes are all reference types, in C#, copying must be done explicitly, somewhere. Here's a solution using an anonymous constructor to copy the value you care about.

    static void Main( string[ ] args )
    {
        Engine v8 = new Engine( );
        v8.Horesepower = 1000; // this changes the field Engine in yugo as well
        Vehicle monsterTruck = new Vehicle( new Engine { Horesepower = v8.Horesepower } );

        Console.WriteLine( v8.Horesepower ); // prints 1000
        Console.WriteLine( monsterTruck.Engine.Horesepower ); // prints 1000

        v8.Horesepower = 800;
        monsterTruck.Engine.Horesepower = 1200;

        Console.WriteLine( v8.Horesepower ); // prints 800
        Console.WriteLine( monsterTruck.Engine.Horesepower ); // prints 1200
    }

Bear in mind this is an easy solution, but it gets cumbersome if you need to make deep copies of any reference classes that the class you're copying might also contain. In that case, the ICloneable solution posted by @TMcKeown is safer and is always more robust (assuming you maintain it!).

7 Comments

In which case anonymous constructors can do it, if it's a one-off situation. I'll put that in this answer.
Wait... Your comment seems to me to be the opposite of what he's asking for. The question is unclear. Is copying the value from the previous instance of A wanted or not? The comment in the question code seems to indicate no.
Sorry, I've edited my C# code to clarify the problem.
it's very clear, he wants to retain the value after cloning... shallow copying doesn't protect it.
Also tied the answer to yours, to point out ICloneable is better in the non-simple (ie more nested ref types) case.
|
0

another way to solve your problem, by using the copy constructors like this

using System;

public class Program
{
    public static void Main()
    {
        A foo = new A();
        B bar = new B(foo);
        foo.data = 20;

        Console.WriteLine(bar.a.data); // prints 9
    }
}

class A
{
    public int data = 9;
    public A()
    {
    }
    public A(A a)
    {
        this.data=a.data;
    }
}
class B
{
    public B(A a_) { a = new A(a_); }
    public A a;
}

hope it will help you

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.