Reputation: 727
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;
}
Upvotes: 3
Views: 269
Reputation: 12847
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;
}
}
Upvotes: 3
Reputation: 6784
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
Upvotes: 0
Reputation: 4657
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!).
Upvotes: 2