.NET: Inheritance Modify Base Class (C++ Conversion)
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.
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?
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.
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.
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.
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. Deep cloning objects
Discussion about Deep cloning Create a Deep Copy in C#
精彩评论