views:

126

answers:

5

I have two classes. The base class is A. The inherited class is B. I would like copy a base class from one object into the base class of another object without affecting the original class. However, .NET seems to ignore the copying. Is this not possible in .NET. I know this is possible in C++. I have included C++ code to illustrate what I am trying to achieve.

I understand in that in this particular example I can directly assign the value bClass.ValueA = aClass.ValueA. But what if Class A had private members? This would not be possible.

Example Classes

Public Class A
    Public ValueA As String

End Class

Public Class B
    Inherits A
    Public ValueB As String

End Class

The Code

    Dim aClass As New A
    Dim bClass As New B

    aClass.ValueA = "AClass"

    bClass.ValueA = "BClass"
    bClass.ValueB = "BClass"

    Dim baseBClass As A
    baseBClass = CType(bClass, A)
    baseBClass = aClass

Results:

aClass.ValueA = "AClass"

bClass.ValueA = "BClass"

bClass.ValueB = "BClass"

baseBClass.ValueA = "AClass"

Intended Results:

aClass.ValueA = "AClass"

bClass.ValueA = "AClass"

bClass.ValueB = "BClass"

baseBClass.ValueA = "AClass"

C++ Explanation Comparison

#include <string>
#include <iostream>

using namespace std;

class A {
public:
    string ValueA;
};

class B : public A {
public:
    string ValueB;
};



int main(int argc, char *argv[]) {
    A aClass;
    B bClass;
    aClass.ValueA = "AClass";

    bClass.ValueA = "BClass";
    bClass.ValueB = "Blcass";

    cout << aClass.ValueA <<endl;
    cout << bClass.ValueA << endl;
    cout << bClass.ValueB << endl;

    A *baseBClass;
    baseBClass = (A*) &bClass;
    *baseBClass = aClass;

    cout << aClass.ValueA <<endl;
    cout << bClass.ValueA << endl;
    cout << bClass.ValueB << endl;
    cout << baseBClass->ValueA << endl;


    return 0;
}

Intended and Actual Results:

aClass.ValueA = "AClass"

bClass.ValueA = "AClass"

bClass.ValueB = "BClass"

baseBClass->ValueA = "AClass"

I do not think this is possible without pointers. I have tried

Ctype(bClass, A) = aClass
Directcast(bClass, A) = aClass

I get this Error:

Expression is a value and therefore cannot be the target of an assignment.

+1  A: 

I just numbered the lines of your code for convinience:

1 Dim aClass As New A
2 Dim bClass As New B

3 aClass.ValueA = "AClass"

4 bClass.ValueA = "BClass"
5 bClass.ValueB = "BClass"

6 Dim baseBClass As A
7 baseBClass = CType(bClass, A)
8 baseBClass = aClass

You're assigned bClass.ValueA the value "BClass" at line 4, so that's what makes your output how it is.

Jeroen
Thanks Jeroen, I've explained a bit more on what I mean.
Shiftbit
I think because at line 8, baseBClass is supposed to be now an A class, when he does the assignment, baseBClass should have the copied value of aClass.
hydrogen
A direct assignment is fine, but what if the base class has private members.
Shiftbit
@Shiftbit: At which line did you expect bClass to get the value you expected it to get?
Jeroen
at Line 8 I would like bClass base class to change to aClass. However it seems to be just writing over the address with aClass. I do not think this is possible in .NET without pointers.
Shiftbit
In order to accomplish that you do need pointers, now you're only working with references. In C# you could use the unsafe keyword to work with pointers. In VB that's not available.
Jeroen
+1  A: 

When you assign baseBClass = aClass you are assigning a pointer that refers to aClass.

This will replace the pointer to bClass that was stored here: baseBClass = CType(bClass, A).

I think you may need to use a copy constructor in A to achieve the desired effect.

hydrogen
Copy Constructor or Composition over inheritance would help, but not if those classes come from a closed library.
Shiftbit
Eek.. Hrm okay well lets just wait until a .NET guru finds this. I'll bump this question up in a bit as i'm interested to see the outcome as well.
hydrogen
+1  A: 
Dim aClass As New A
Dim bClass As New B

aClass.ValueA = "AClass"

bClass.ValueA = "BClass"
bClass.ValueB = "BClass"

Okay, so now you have two instances allocated on the heap, and two variables that refer to them. Something like this:

Variable       Instances on the heap
--------       ---------------------

aClass ------> Instance of class A

bClass ------> Instance of class B

(Your naming is confusing, by the way. aClass and bClass aren't classes at all; they're variables that refer to instances.)

Then you do this:

Dim baseBClass As A
baseBClass = CType(bClass, A)

So now you have three variables, but the same two instances as before. Two of the variables (bClass and baseBClass) both refer to the same instance:

Variable          Instances on the heap
--------          ---------------------

aClass ---------> Instance of class A

baseBClass -\
             >--> Instance of class B
bClass -----/

The baseBClass variable is of a different type than the bClass variable, but they both refer to the same instance.

Now you do this:

baseBClass = aClass

Which replaces the reference in the baseBClass variable with a different reference, specifically, a reference to the thing labeled "Instance of class A" above:

Variable          Instances on the heap
--------          ---------------------

aClass -----\
             >--> Instance of class A
baseBClass -/

bClass ---------> Instance of class B

You're just assigning references around. You never actually copy any data.

But from what I gather of your description, you expect some of these assignments to copy references from one variable to another, and other assignments to copy the objects' contents from one instance to another? You're using the same assignment operator to mean both things. How do you expect the compiler to know which is which?

Joe White
A: 

If I'm understanding you correctly, what you're trying to do is to rip part of the state out of one object instance, and shove it down another instance's throat. I.e., you want to violate encapsulation.

There are ways to accomplish this that actually work and that don't violate encapsulation, but honestly, I think this smells like a really bad design -- one that C++ was willing to let you get away with, but a bad design nonetheless. It sounds like you're trying to abuse inheritance, when you really should be using aggregation. You're saying that "an instance of B is an A", when that isn't actually appropriate. Instead, it should be "an instance of B has an A". Or to express it in code:

Public Class A
    Public ValueA As String
End Class

Public Class B
    Public A As A
    Public ValueB As String
End Class

Note that B no longer descends from A; instead it has a reference to an instance of A. When you create a B, you would need some way to give it an instance of A, so you could initialize that field -- perhaps B's constructor could create an A and assign the reference into the field, or perhaps you pass an instance of A to B's constructor. Later, if you decide it's referring to the wrong instance of A, you could change it to refer to a different instance.

Joe White
As discussed with hydrogen Composition is a good alternative but not viable if this is a 3rd party closed library.
Shiftbit
A: 

One alternative is to have a C# class that does the pointer manipulation and adds extension method to the B Class. It would be very similar to the C++ solution. This seems the best solution as it would be almost identical to the C++ implementation.

Instead I used reflection. It is only a shallow copy but in this case it does not matter. C++ default copy constructor is only a shallow clone. With some embellishment you can check to see if IClonable exists and attempt to clone it, otherwise instantiate a new object. I have updated this to include private members.


Imports System.Runtime.InteropServices
Imports System.Reflection

Public Class A
    Private ValueA As String
    Public Sub New(ByVal ValueA As String)
        Me.ValueA = ValueA
    End Sub
    Public Overrides Function ToString() As String
        Return String.Format("{0}:{1}={2}", _
        Me.GetType.Name, "ValueA", ValueA)
    End Function
End Class

Public Class B
    Inherits A
    Private ValueB As String
    Public Sub New(ByVal ValueA As String, ByVal ValueB As String)
        MyBase.New(ValueA)
        Me.ValueB = ValueB
    End Sub
    Public Overrides Function ToString() As String
        Return String.Format("{0}:{1}={2},{3}", _
        Me.GetType.Name, "ValueB", ValueB, MyBase.ToString)
    End Function
End Class

Module Inheritence

    Sub Main()
        Dim aClass As New A("AClass")
        Dim bClass As New B("BClass", "BClass")

        Console.WriteLine(aClass)
        Console.WriteLine(bClass)

        For Each item As FieldInfo In aClass.GetType.GetFields( _
                                             BindingFlags.Public _
                                             Or BindingFlags.Instance _
                                             Or BindingFlags.NonPublic _
                                             Or BindingFlags.Static)
            item.SetValue(bClass, item.GetValue(aClass))
        Next
        Console.WriteLine(aClass)
        Console.WriteLine(bClass)
        Console.ReadKey()
    End Sub

End Module

Base Class Clone

This is the site I based my solution upon. It is a shallow clone but easy and quick to implement http://www.codeproject.com/KB/cs/cloneimpl_class.aspx

This solution is better as it attempts to do a deep clone on objects that support IClonable otherwise it just sets a new instance. http://whizzodev.blogspot.com/2008/03/object-cloning-using-il-in-c.html

Clone Routines:

This is an interesting site that explains various cloning techniques. http://developerscon.blogspot.com/2008/06/c-object-clone-wars.html

Another interesting Resource. http://stackoverflow.com/questions/78536/cloning-objects-in-c

Discussion about Deep cloning http://stackoverflow.com/questions/3647048/create-a-deep-copy-in-c/3651295#3651295

Shiftbit