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;
}
dodexahedron
  • 4,584
  • 1
  • 25
  • 37
user22119
  • 717
  • 1
  • 4
  • 18
  • 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.) http://msdn.microsoft.com/en-us/library/system.icloneable%28v=vs.110%29.aspx – xxbbcc Dec 31 '14 at 16:06
  • 1
    From what I know, IClonable doesn't differentiate between shallow and deep clones. – user22119 Dec 31 '14 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. – xxbbcc Dec 31 '14 at 16:08
  • Clone doesn't perform deep copies on ref type properties.. – T McKeown Dec 31 '14 at 16:12
  • `ICloneable.Clone` doesn't do anything. It depends on the implementation. – Olivier Jacot-Descombes Dec 31 '14 at 16:23
  • ofcourse I was referring to MemberwiseClone() – T McKeown Dec 31 '14 at 16:27
  • Thanks for the question edit. That clarified the behavior you want. Yes, cloning is the way to go, and you must do it explicitly in some way, be it via MemberwiseClone() or by a copy constructor. – dodexahedron Dec 31 '14 at 16:44
  • You may want to rethink this. C# is not C++, and there are reasons for that. – John Saunders Dec 31 '14 at 18:12

3 Answers3

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;
    }
}
Community
  • 1
  • 1
T McKeown
  • 12,971
  • 1
  • 25
  • 32
  • 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. – user22119 Dec 31 '14 at 16:18
  • it is used quite a lot, if you don't want to do serialization then you need to write a copy constructor. – T McKeown Dec 31 '14 at 16:19
  • Serialization is a very heavy hammer for cloning. Should be a last resort, if you have control over all the code. – dodexahedron Dec 31 '14 at 16:35
  • 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. – user22119 Dec 31 '14 at 16:42
  • 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. – T McKeown Dec 31 '14 at 16:44
  • As @TMcKeown said, just a tradeoff in efficiency vs flexibility (depending on how you serialize, as an exact type match would still fail in deserialization, but that's an easy problem to get around). – dodexahedron Dec 31 '14 at 16:47
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!).

dodexahedron
  • 4,584
  • 1
  • 25
  • 37
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

Monah
  • 6,714
  • 6
  • 22
  • 52