Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

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.

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(objIn, 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 {}

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();
    }

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.

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.

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
  }
}

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!

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";

Please don't downvote me because you think the above is stupid! 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. :)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top