views:

118

answers:

7

This is easier to explain with an example. Given these two classes:

public class MyClassA
{
    public String Property_A { get; set; }
    public String Property_B { get; set; }
    public String Property_C { get; set; }
    public String Property_D { get; set; }
    ...
    public String Property_Y { get; set; }
}

public class MyClassB: MyClassA
{
    public String Property_Z { get; set; }
}

Suppose I have fully created instance of MyClassA (with all properties from A - Y filled in). Then I need to make an instance of MyClassB which is exactly the same as my instance of MyClassA but with Property_Z filled in (with a custom value of course). How can I do this?

Doing this does not work (throws and Invalid Cast Exception):

MyClassB myInstanceB = (myClassB) myInstanceA;
myInstance.Property_Z = myCustomValue;

I have not needed to do anything like this since my C++ days and I am stumped.

Any ideas?


UPDATE: I found a solution to my problem in how I create the instances. It is below. I did not mark it as the answer because it did not exactly fit my question.

+2  A: 

You can use Reflection to copy base class properties as shown here.

 public void Update(MyObject o)
    {
        MyObject copyObject = ...
        Type type = o.GetType();
        while (type != null)
        {
            UpdateForType(type, o, copyObject);
            type = type.BaseType;
        }
    }


    private static void UpdateForType(Type type, MyObject source, MyObject destination)
    {
        FieldInfo[] myObjectFields = type.GetFields(
            BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

        foreach (FieldInfo fi in myObjectFields)
        {
            fi.SetValue(destination, fi.GetValue(source));
        }
    }
NebuSoft
+1  A: 

You could define an explicit or implicit cast from MyClassA to MyClassB and use the syntax you've provided.

public class MyClassB : MyClassA
{
    public String Property_Z { get; set; }

    public static explicit operator MyClassB(MyClassA a)
    {
        MyClassB b = new MyClassB();
        b.Property_A = a.Property_A;
        /* ... */
        b.Property_Y = a.Property_Y;
        return b;
    }
}
Jake
+5  A: 

The instance you've created is a MyClassA. That is its runtime type, not MyClassB. You cannot cast a MyClassA instance to a MyClassB at runtime because MyClassB is a more specific type than MyClassA.

You need to create a brand-new instance of MyClassB. One way to clean this up is to create a constructor that takes a MyClassA, e.g.

public class MyClassB : MyClassA
{
    public MyClassB(MyClassA a, string z)
    {
        this.PropertyA = a.PropertyA;
        this.PropertyB = a.PropertyB;
        // etc.
        this.PropertyZ = z;
    }

    public string PropertyZ { get; set; }
}
Aaronaught
I did not say it, but I was hoping to avoid having to copy all of the items from one instance to another.
Vaccano
@Vaccano: If you find yourself having to do this all over the place then I would reconsider your design; however, if you desperately need to speed up the process then you could use Reflection or a Reflection-based mapper such as AutoMapper - just know that the time you save coding will be offset by increased execution times in the program (how much time depends on how often you do it).
Aaronaught
Yeah, I don't want to slow down execution either. I found a compromise (see my answer to this question) by changing how I setup myInstanceA to use type parameters. I am not converting myInstanceA to a MyClassB like I hoped but it works. (I will not set my answer as the "answer" because it does not quite match the question I asked.)
Vaccano
+1  A: 

The straightforward answer:

public class MyClassA 
{ 
    public String Property_A { get; set; } 
    public String Property_B { get; set; } 
    public String Property_C { get; set; } 
    public String Property_D { get; set; } 
    ... 
    public String Property_Y { get; set; } 
} 

public class MyClassB: MyClassA 
{ 
    public MyClassB(MyClassA copy)
    {
        Property_A = copy.PropertyA;
        Property_B = copy.PropertyB;
        ...
    }
    public String Property_Z { get; set; } 
} 

Use it like this:

MyClassB o = new MyClassB(instanceOfMyClassA);
o.Property_Z = whatever;
John Fisher
+2  A: 

Sounds like you're looking for a free copy constructor. C# doesn't supply one (http://msdn.microsoft.com/en-us/library/ms173116%28VS.80%29.aspx) but you can do it pretty easily with Object.MemberwiseClone or the BinaryFormatter serializer (http://www.codeproject.com/KB/tips/SerializedObjectCloner.aspx). Take care to know if you want a shallow copy or deep copy.

Crispy
A: 

What about:

  1. Create a base class that implements IClonable and has all the string properties A to D, and Z.
  2. Create instance of this class for MyClassA.
  3. Create instance of MyClassB by cloning MyClassA.
  4. Set property Z in MyClassB.
Nick Miller
A: 

Here is what I ended up doing:

I made my method that creates and sets up all the properties (A-Y) that uses type parameters. It looks like this:

public T MakeAMyClass<T>(AnotherClass 
where T: MyClassA : new()
{
   T returnValue = new T();
   T.Property_A = somethingToSetFrom.MakeAPropertyA();
   // Fill in the rest of the properties
}

Because a type parameter can be a type that is descended from the constraint, I am able to pass in MyClassB objects and get them made the same way as myClassA.

Then I can make either one as needed.

MyClassA myInstanceA = MakeAMyClass<MyClassA>(somethingToSetFrom);
MyClassB myInstanceB = MakeAMyClass<MyClassB>(somethingToSetFrom);
myInstanceB.Property_Z = someotherValue;

The lets me avoid having to make a method to copy all the properties from MyClassA to MyClassB.

Vaccano