26

I have a base class

public class A   
{
    public string s1;
    public string s2;
}

I also have a derived class :

public class B : A
{
    public string s3;
}

Suppose my program created an instance of class A.

A aClassInstance = new A();

some parameters were set:

aClassInstance.s1 = "string 1";
aClassInstance.s2 = "string 2";

At this point I would like to create an instance of class B. But I would like B to already have the values of my instance of class A.

This DID NOT Work:

public B bClassInstance = new B():
bClassInstance = (B)aClassInstance;

NEITHER DID THIS:

Made a clone method within Class A.

public B cloneA() {    
    A a = new A();
    a = (A)this.MemberwiseClone()
    return(B)a;
}

The VS code takes both of the above - but I get run-time errors

Please help

Jan
  • 15,802
  • 5
  • 35
  • 59
Sam
  • 946
  • 3
  • 11
  • 22
  • 2
    Be careful when cloning - in particular if your class has fields of mutable reference types. Decide if you want a deep clone or a shallow clone, and document it. – TrueWill Dec 25 '11 at 22:39
  • 1
    right. This particular class has no references, so a shallow clone works for it. I found a good post on shallow vs deep cloning here for anyone interested: http://itpksingh.blogspot.com/2009/08/shallow-copyingdeep-copyingobject.html – Sam Dec 26 '11 at 00:22
  • Found a solution using ValueInjector. StackOverFlow is not allowing me to "answer my own question" yet. Once it does, will post full details. – Sam Dec 26 '11 at 01:35
  • Does this answer your question? [Is it possible to assign a base class object to a derived class reference with an explicit typecast?](https://stackoverflow.com/questions/729527/is-it-possible-to-assign-a-base-class-object-to-a-derived-class-reference-with-a) – Michael Freidgeim Jan 05 '23 at 23:34

4 Answers4

36

The base problem you have is, that you have to construct an instance of type B (which contains of cause the properties of type A). Your approach to clone an A instance won't work, because that gives you an instance of type A, which you can't convert to B.

I would write constructors for class A and B which takes a parameter of type A. The constructor of class B just passes the value to its base class A. The constructor of class A knows how to copy the fields to itself:

class A {
    public A(A copyMe) {
        s1 = copyMe.s1;
        ...
    }

class B : A {

    public B(A aInstance) : base(aInstance) {
    }

}

Use it this way:

A a = new A();
a.s1 = "...";

B b = new B(a);

EDIT

When you don't want to have to change the constructor of A when adding new fields or props, you could use reflection to copy the properties. Either use a custom attribute to decorate what you want to copy, or copy just all props/fields of A:

public A (A copyMe) {
    Type t = copyMe.GetType();
    foreach (FieldInfo fieldInf in t.GetFields())
    {
        fieldInf.SetValue(this, fieldInf.GetValue(copyMe));
    }
    foreach (PropertyInfo propInf in t.GetProperties())
    {
        propInf.SetValue(this, propInf.GetValue(copyMe));
    }
}

I havn't tried the code, but the point should become clear.

Jan
  • 15,802
  • 5
  • 35
  • 59
  • I am trying to upgrade this so each member doesn't have to be copied individually:class A { public A(A copyMe) { s1 = copyMe.s1; the (A)copyMe.MemberwiseClone() should return a shallow clone of my call A that I need. Now all we need to do is pass it back to the B constructor... question is how. unfortunately we can't do this: Class A{ public A(A copyMe){ this = (A)copyMe.MemberwiseClone() } } ... } – Sam Dec 26 '11 at 00:47
  • 1
    @Sam: The problem is, that you have to construct an object of type B. Because A is part of B, you have to find a way of settings the properties of the new B instance, that belong to the type A. Your *clone A* approach won't workt because that gives you an instance of type A, where you need an instance of type B! What you can do, is to use reflection in the constructor of A. I will update my answer to reflect that. – Jan Dec 26 '11 at 11:58
  • The reflection approach will throw if a property does not have a setter. Checking for propInf.CanWrite solves the problem – Bill Tarbell Sep 12 '18 at 17:09
10

You could create a generic clone method in class A:

     public T Clone<T>() where T : A, new() {
          return new T() { a = this.a, b = this.b};
     }

Or if you want to make the cloning extendable:

     public T Clone<T>() where T : A, new() {
          var result = new T();
          this.CopyTo(result);
          return result;
     }

     protected virtual void CopyTo(A other) {
          other.a = this.a;
          other.b = this.b;
     }

You use it like this:

     A  a = new A();
     // do stuff with a
     // Create a B based on A:
     B b = a.Clone<B>();

Please note: in your example, both the new A(), and the MemberwiseClone will create a new object of type A.

If you do not want to code the copy method yourself, you could look at a tool like AutoMapper.

GvS
  • 52,015
  • 16
  • 101
  • 139
  • Thanks! In this example, would I have to set all of members (like a = this.a, b = this.b, etc.). If the class has multiple members, anyway to avoid coding each one? (if i decide to add another member, I would have to set it in the cloning function as well) – Sam Dec 26 '11 at 00:21
  • You could build something with reflection (loop though all fields, and copy them), but from experience I know, you'd better control the copying yourself. Sometimes you need a shallow copy, for some objects you need a deep copy. – GvS Dec 26 '11 at 08:39
  • Why doesn't `other = (A)this.MemberwiseClone();` work i nthe above (instead of having to set each property). I'm trying to avoid setting each property or use reflection. – PeterX Mar 03 '15 at 00:20
4

After playing around and reading everything I could get my eyes on, both of the above solutions by GvS and Jan work. However, the end result that I wanted to achieve is not to be forced to write out each member in the Copy methods.

Why: a) If the class is edited and another object is added, the copy method will have to be updated. If someone else updates the class, they may forget to do this.

b) There may be a lot of members, and assigning them may be time consuming.

c) It just doesn't "feel" right. (Probably because I am very lazy).

Fortunately, I am not the only one with the same thoughts. Found a very very easy solution via the ValueInjector. (it has been discussed on these boards a lot).

After getting the dll (http://valueinjecter.codeplex.com/documentation)

The code becomes:

A a = new A();
a.s1 = "...";


B b = new B();
b.InjectFrom(a);

That's it :)

Obviously you would have to include:

using Omu.ValueInjecter;

And not forget to add it to the references.

Sam
  • 946
  • 3
  • 11
  • 22
2

You can also use a JSON serializer for example. You add a static method to your child-class which could then be called like this:

var porsche = Porsche.FromCar(basicCar);

Here, "Porsche" is the child class and "Car" is the base class. The function would then look something like this:

public class Porsche : Car
{
    public static Porsche FromCar(Car basicCar)
    {
        // Create a JSON string that represents the base class and its current values.
        var serializedCar = JsonConvert.SerializeObject(basicCar);

        // Deserialize that base class string into the child class.
        return JsonConvert.DeserializeObject<Porsche>(serializedCar);
    }

    // Other properties and functions of the class...
}

The trick here is, that properties that are available in the child but not the base, will be created with their default value, so null usually, depending on the type of the property. The deserialization also goes by the name of the property, so all properties are copied over.

I didn't test this code, but it should work, as I've done this once or twice before. Hope it helps someone.

Sven Möhring
  • 770
  • 13
  • 22