user22119
user22119

Reputation: 727

How to get a class object to store another class object in c#?

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

Answers (3)

T McKeown
T McKeown

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

Monah
Monah

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

dodexahedron
dodexahedron

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

Related Questions