29

I have a class A and a class B that inherits class A and extends it with some more fields.

Having an object a of type A, how can I create an object b of type B that contains all data that object a contained?

I have tried a.MemberwiseClone() but that only gives me another type A object. And I cannot cast A into B since the inheritance relationship only allows the opposite cast.

What is the right way to do this?

sampathsris
  • 21,564
  • 12
  • 71
  • 98
Vizu
  • 1,871
  • 1
  • 15
  • 21

10 Answers10

11

There is no means of doing this automatically built into the language...

One option is to add a constructor to class B that takes a class A as an argument.

Then you could do:

B newB = new B(myA);

The constructor can just copy the relevant data across as needed, in that case.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
11

I would add a copy constructor to A, and then add a new constructor to B that takes an instance of A and passes it to the base's copy constructor.

Bryan
  • 3,453
  • 6
  • 26
  • 20
  • 2
    The accepted answer is what I've always done, but this twist is simple and elegant. – JMD Oct 29 '14 at 03:51
5

You can achieve this by using reflection.

Advantage: Maintainability. No need for changing copy-constructor or similar, adding or removing properties.

Disadvantage: Performance. Reflection is slow. We're still talking milliseconds on average sized classes though.

Here's a reflection-based shallow copy implementation supporting copy-to-subclass, using extension methods:

public static TOut GetShallowCopyByReflection<TOut>(this Object objIn) 
{
    Type inputType = objIn.GetType();
    Type outputType = typeof(TOut);
    if (!outputType.Equals(inputType) && !outputType.IsSubclassOf(inputType)) throw new ArgumentException(String.Format("{0} is not a sublcass of {1}", outputType, inputType));
    PropertyInfo[] properties = inputType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    FieldInfo[] fields = inputType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
    TOut objOut = (TOut)Activator.CreateInstance(typeof(TOut));
    foreach (PropertyInfo property in properties)
    {
        try
        {
            property.SetValue(objOut, property.GetValue(objIn, null), null);
        }
        catch (ArgumentException) { } // For Get-only-properties
    }
    foreach (FieldInfo field in fields)
    {
        field.SetValue(objOut, field.GetValue(objIn));
    }
    return objOut;
}

This method will copy all properties - private and public, as well as all fields. Properties are copied by reference, making it a shallow copy.

Unit tests:

[TestClass]
public class ExtensionTests {
    [TestMethod]
    public void GetShallowCloneByReflection_PropsAndFields()
    {
        var uri = new Uri("http://www.stackoverflow.com");
        var source = new TestClassParent();
        source.SomePublicString = "Pu";
        source.SomePrivateString = "Pr";
        source.SomeInternalString = "I";
        source.SomeIntField = 6;
        source.SomeList = new List<Uri>() { uri };

        var dest = source.GetShallowCopyByReflection<TestClassChild>();
        Assert.AreEqual("Pu", dest.SomePublicString);
        Assert.AreEqual("Pr", dest.SomePrivateString);
        Assert.AreEqual("I", dest.SomeInternalString);
        Assert.AreEqual(6, dest.SomeIntField);
        Assert.AreSame(source.SomeList, dest.SomeList);
        Assert.AreSame(uri, dest.SomeList[0]);            
    }
}

internal class TestClassParent
{
    public String SomePublicString { get; set; }
    internal String SomeInternalString { get; set; }
    internal String SomePrivateString { get; set; }
    public String SomeGetOnlyString { get { return "Get"; } }
    internal List<Uri> SomeList { get; set; }
    internal int SomeIntField;
}

internal class TestClassChild : TestClassParent {}
Nilzor
  • 18,082
  • 22
  • 100
  • 167
  • What counts as average class size? – Adam L. S. Jan 29 '14 at 22:25
  • @adam-l-s Hehe good question. My answer as always when it comes to performance: Measure. If it's fast enough for you, use it. Reflection is said to be 1000 times slower than accessing properties the normal way though: http://stackoverflow.com/questions/25458/how-costly-is-net-reflection – Nilzor May 23 '14 at 07:06
  • I know this is a six year old answer, but there is a bug in the code. The line that says: `property.SetValue(objIn, property.GetValue(objIn, null), null);` should be: `property.SetValue(objOut, property.GetValue(objIn, null), null);` Otherwise the properties are just set back on the original object and not the new object. Just trying to help out some copy-pasters out there. – briandunnington Mar 12 '20 at 20:51
  • I note that the performance issues can be eliminated completely if you're comfortable with `Reflection.Emit` to generate a clone method on-demand. – Dai Jun 25 '20 at 13:17
4

Using Factory Method Pattern:

    private abstract class A
    {
        public int P1 { get; set; }

        public abstract A CreateInstance();

        public virtual A Clone()
        {
            var instance = CreateInstance();
            instance.P1 = this.P1;
            return instance;
        }
    }

    private class B : A
    {
        public int P2 { get; set; }

        public override A CreateInstance()
        {
            return new B();
        }

        public override A Clone()
        {
            var result = (B) base.Clone();
            result.P2 = P2;
            return result;
        }
    }

    private static void Main(string[] args)
    {
        var b = new B() { P1 = 111, P2 = 222 };

        var c = b.Clone();
    }
JoanComasFdz
  • 2,911
  • 5
  • 34
  • 50
1

Create a ctor in B that allows one to pass in an object of type A, then copy the A fields and set the B fields as appropriate.

Michael Todd
  • 16,679
  • 4
  • 49
  • 69
0

You could make a Convert method on class B that takes in the base class.

public ClassB Convert(ClassA a)
{
   ClassB b = new ClassB();
   // Set the properties
   return b;
}

You could also have a constructor for ClassB take in an object of ClassA.

Brandon
  • 68,708
  • 30
  • 194
  • 223
0

No, you can't do that. One way to achieve this is to add a constructor on class B that accepts a parameter of type B, and add data manually.

So you could have something like this:

public class B
{
  public B(A a)
  {
    this.Foo = a.foo;
    this.Bar = a.bar;
    // add some B-specific data here
  }
}
Razzie
  • 30,834
  • 11
  • 63
  • 78
  • 3
    I disagree that you should have a Clone() method on A that returns a B, as this introduces a circular dependency. – Matt Howells Jul 07 '09 at 15:44
  • I agree with the constructor for class B, but why do you need the CloneToB() method? – Jon Cage Jul 07 '09 at 15:49
  • ehm yeah you're right, I shouldn't have included that method. I included it because the poster mentioned MemberWiseClone(). Anyway, this code is not good and I will delete it. Thanks. – Razzie Jul 07 '09 at 19:28
0

In your base class add the CreateObject virtual method below...

    public virtual T CreateObject<T>()
    {
        if (typeof(T).IsSubclassOf(this.GetType()))
        {
            throw new InvalidCastException(this.GetType().ToString() + " does not inherit from " + typeof(T).ToString());
        }

        T ret = System.Activator.CreateInstance<T>();

        PropertyInfo[] propTo = ret.GetType().GetProperties();
        PropertyInfo[] propFrom = this.GetType().GetProperties();

        // for each property check whether this data item has an equivalent property
        // and copy over the property values as neccesary.
        foreach (PropertyInfo propT in propTo)
        {
            foreach (PropertyInfo propF in propFrom)
            {
                if (propT.Name == propF.Name)
                {
                    propF.SetValue(ret,propF.GetValue(this));
                    break;
                }
            }
        }

        return ret;
    }

then say you want to create a real life subclass object from the super class just call

this.CreateObject<subclass>();

That should do it!

Hoots
  • 1,876
  • 14
  • 23
0

While no one suggested this (and this won't work for everyone, admittedly), it should be said that if you have the option of creating object b from the get-go, do that instead of creating object a then copying to object b. For example, imagine that you are in the same function and have this code:

var a = new A();
a.prop1 = "value";
a.prop2 = "value";
...
// now you need a B object instance...
var b = new B();
// now you need to copy a into b...

Instead of worrying about that last commented step, just start with b and set the values:

var b = new B();
b.prop1 = "value";
b.prop2 = "value";

I appreciate that not everyone will like the above. I have encountered many programmers who are so focused on their code that they didn't realize a simpler solution is staring them in the face. :)

halfer
  • 19,824
  • 17
  • 99
  • 186
Jazimov
  • 12,626
  • 9
  • 52
  • 59
-1

here's a way that worked for me using a constructor:

class ClassA():
    def __init__(self, **attrs):
        self.__dict__.update(**attrs)
        ...

b = ClassA(**other_class.__dict__)

can also work with inheritance

class Fields(AbstractType):
    def __init__(self, **attrs):
        self.__dict__.update(**attrs)

    my_date = Field(Date, required=True)
    text_field = Field(String, required=True)
    ...

class MyClass(InputObjectType, Fields):
    pass


class MyClassWithError(ObjectType, Fields):
    error = Field(String, required=True)


error_class = MyClassWithError(**my_class.__dict__)
error_class.error = "my error description"
Sonic Soul
  • 23,855
  • 37
  • 130
  • 196