Question OO question: how to cast from a base class to a derived class?

brendan.hill

Member
Joined
Jun 20, 2010
Messages
15
Programming Experience
10+
OK I know this isn't supposed to be possible, because you can't be sure the object is really of the derived class (can't cast a mammal as a cat because it might be a dog, etc).

The thing is, I have a legitimate need to achieve this somehowand see no problem in this circumstance. I'm writing a simplified interface for XmlDocument, and I want to add my own functions, but inherit XmlDocument so I retain all the functionality.

When I try this code:

VB.NET:
Public Class SimpleXmlDoc
    Inherits System.Xml.XmlDocument
    
    Public Function addChild(ByVal anything As Object) As SimpleXmlElement
        Return Me.AppendChild(AnythingToElement(anything, Me))
    End Function
.
.
Public Class SimpleXmlElement
    Inherits System.Xml.XmlElement
.
.

It can't cast from the base type XmlElement (which Me.AppendChild returns) to the derived type SimpleXmlElement (which is the return type of the function).

How can I do this? There must be some way to cast "down" in a controlled way, so you can wrap & extend classes like this.

-Brendan


One answer is not to inherit at all, but to wrap and expose each member one by one, but this seems tedious.
 
Alternatively, can I get a derived class to set it's own MyBase? This way, SimpleXmlElement could accept XmlElement as a constructor parameter, set MyBase, and continue as normal. I don't really see why this shouldn't be possible. For example this would be ideal:

VB.NET:
Public Class SimpleXmlElement
    Inherits System.Xml.XmlElement
    Public Sub New(ByVal x As System.Xml.XmlElement)
        MyBase = x
.
.
    End Sub




Failing that, can anyone recommend a way to extend & override the functionality XmlDocument without having to wrap & redeclare each member?
 
Also for the record I have tried casting functions:

VB.NET:
    Public Shared Narrowing Operator CType(ByVal XmlElement As System.Xml.XmlElement) As SimpleXmlElement
.
.
    End Operator

I'm not sure what this contain, and anyway it produces this error:

"Conversion operators cannot convert from a base type"
 
Of course you can cast a Mammal as type Cat. That's the whole point of casting: to get a reference of a more specific type from a reference of a less specific type. Let's say that I go to a vet who specialises in mammals and the vet calls me into the surgery and I take my box in. The vet knows that I have a mammal in there but they don't know more than that. If I tell the vet that the mammal I have in the box is a cat then they now knows that they can do cat things rather than just mammal things. That is a cast:
Dim theCat As Cat = DirectCast(theBox.Mammal, Cat)
Casting doesn't do anything to the object itself. It simply changes the type of the reference being used to access it. Have you ever heard the expression "to vast something in a different light"? That means that you look at the same object in a different way and therefore are able to see things about it that you couldn't before. That is exactly where the programming term cast comes from because it does the very same thing. The object is still the very same object it was but you are now accessing it via a reference of a different type so you are now able to access members that you couldn't before. In this case:
Dim myMammal As Mammal = New Cat
Dim myCat As Cat = DirectCast(myMammal, Cat)
is the object ever not a Cat? Of course not. If you were to use the myMammal variable to access it though, you would only be able to access members of the Mammal class because all you know about the object (more precisely, all the compiler knows about the object) is that it is a Mammal.

I think the issue that you're talking about has nothing to do with casting, but rather converting, which is something different. A conversion actually does change the type of the object, which is not always possible. If your AppendChild method returns an XmlElement reference then the compiler will absolutely allow you to cast it as type SimpleXmlElement because that is a valid cast. At run time though, if the object that the method actually returns is not a SimpleXmlElement object (or a type derived from that) then an exception will be thrown because you if the object itself is not a SimpleXmlElement then how can it be assigned to a SimpleXmlElement variable?

You cannot take an XmlElement object and magically turn it into SimpleXmlElement object. When you "create" a cat, do you get to choose at that point whether it's a mammal, a bird, a fish or whatever? Of course not. A cat is a mammal. An object can't decide when it's created what it's base type is going to be. Would it make any sense for a Button to decide that it wasn't a control when it was created? An object is what it is when you create it.

If SimpleXmlElement inherits XmlElement then presumably SimpleXmlElement has additional members. How exactly are those members supposed to be populated if an XmlElement magically became a SimpleXmlElement? If you can answer that question then you write code to create a SimpleXmlElement from an XmlElement, maybe using what's called a "copy constructor", e.g.
Public Class DerivedType
    Inherits BaseType

    Public Sub New(source As BaseType)
        'Populate inherited properties from the source.
        Me.Property1 = source.Property1
        Me.Property2 = source.Property2

        'Initialise other properties.
        Me.Property3 = property3DefaultValue
        Me.Property4 = property4DefaultValue
    End Sub
 
If SimpleXmlElement inherits XmlElement then presumably SimpleXmlElement has additional members. How exactly are those members supposed to be populated if an XmlElement magically became a SimpleXmlElement?

In fact there are no additional variables, only additional functions and subroutines. SimpleXmlElement is simply the addition of some shortcut functions to XmlElement, rather than adding any functionality.


If you can answer that question then you write code to create a SimpleXmlElement from an XmlElement, maybe using what's called a "copy constructor", e.g.
Public Class DerivedType
    Inherits BaseType

    Public Sub New(source As BaseType)
        'Populate inherited properties from the source.
        Me.Property1 = source.Property1
        Me.Property2 = source.Property2

        'Initialise other properties.
        Me.Property3 = property3DefaultValue
        Me.Property4 = property4DefaultValue
    End Sub

Using this approach, I would effectively need to load the XML of the XmlElement passed in the constructor into the SimpleXmlElement which would be inappropriate... but this technique might work in other circumstances.



I'll try the DirectCast approach tomorrow and see if it works. As the internal state of XmlElement and SimpleXmlElement should be identical (SimpleXmlElement doesn't add any new private members) it might just work.
 
As the internal state of XmlElement and SimpleXmlElement should be identical (SimpleXmlElement doesn't add any new private members) it might just work.

There is no "might" about it. Either the object is a SimpleXmlElement or not. If it is then a cast will work and if it's not then it won't. There's no magic about casting. Like I said, it doesn't affect the object in any way. You can only cast as a type that the object is, inherits or implements. You can cast a SimpleXmlElement as type XmlElement because every SimpleXmlElement is an XmlElement. You can only cast a Mammal as type Cat if it is a Cat. If it's a Dog then the cast will obviously fail. If it was just a Mammal then that would fail too, but while such a situation is possible in programming it is not possible in the real world. There's no such thing as just a mammal.
 
There is no "might" about it. Either the object is a SimpleXmlElement or not. If it is then a cast will work and if it's not then it won't. There's no magic about casting. Like I said, it doesn't affect the object in any way. You can only cast as a type that the object is, inherits or implements. You can cast a SimpleXmlElement as type XmlElement because every SimpleXmlElement is an XmlElement. You can only cast a Mammal as type Cat if it is a Cat. If it's a Dog then the cast will obviously fail. If it was just a Mammal then that would fail too, but while such a situation is possible in programming it is not possible in the real world. There's no such thing as just a mammal.

Well the issue then is that what I"m casting from (XmlElement) actually isn't a SimpleXmlElement - that's the whole point. I'm taking the response from XmlElement.appendChild() (which is XmlElement) and I want to return it as SimpleXmlElement (derived class).
 
Well you can't. If it's not a SimpleXmlElement then it's not a SimpleXmlElement. Like I said, casting doesn't change the type of the object so, if the object isn't a SimpleXmlElement, you can't cast as that type. As I also said, if you want a SimpleXmlElement and you haven't got one then you need to create one. I have also demonstrated one way of doing that. You aren't going to magic a SimpleXmlElement no matter how much you want to. If you want one then you have to create one. Just keep in mind that if you create a new SimpleXmlElement and return it then you are not returning the same object as was returned by AppendChild.
 
Well you can't. If it's not a SimpleXmlElement then it's not a SimpleXmlElement. Like I said, casting doesn't change the type of the object so, if the object isn't a SimpleXmlElement, you can't cast as that type. As I also said, if you want a SimpleXmlElement and you haven't got one then you need to create one. I have also demonstrated one way of doing that. You aren't going to magic a SimpleXmlElement no matter how much you want to. If you want one then you have to create one. Just keep in mind that if you create a new SimpleXmlElement and return it then you are not returning the same object as was returned by AppendChild.

Thanks for the clarification, in the end I've gone with a wrapper class (private declaration of XmlElement instead of inheritance) which loses all the native functionality of XmlElement, unfortunately.
 
brendan.hill said:
Public Function addChild(ByVal anything As Object) As SimpleXmlElement
Return Me.AppendChild(AnythingToElement(anything, Me))
End Function
When AnythingToElement returns an instance of SimpleXmlElement class, that would be an object that is valid to cast as type SimpleXmlElement. The AppendChild call you have wrapped around it does not affect that.
You can override XmlDocument.CreateElement method to create different types of element objects.
 
Back
Top