views:

166

answers:

3

Consider the following classes representing an Ordering system:

Public Class OrderBase
    Public MustOverride Property OrderItem as OrderItemBase
End Class

Public Class OrderItemBase
End Class

Now, suppose we want to extend these classes to a more specific set of order classes, keeping the aggregate nature of OrderBase:

Public Class WebOrder
    Inherits OrderBase        

    Public Overrides Property OrderItem as WebOrderItem 
    End Property
End Class

Public Class WebOrderItem
    Inherits OrderItemBase
End Class

The Overriden property in the WebOrder class will cause an error stating that the return type is different from that defined in OrderBase... however, the return type is a subclass of the type defined in OrderBase. Why won't VB allow this?

+3  A: 

You cant do that- its changing the signature defined on the base. To do what you are trying to do you need to use generics:

Public Class OrderBase(Of T As IOrderItem)
    Public ReadOnly Property OrderItems As IList(Of T)
End Class

my vb is rusty so hopefully that is accutrate....

J Rothe
Your syntax was okay, but by going to generic you missed one thing: you no longer need to override the property -- the base implementation will already be correct. Plus, now we'll probably be able to use an interface rather than a base class for the orderItems. I cleaned it up for you.
Joel Coehoorn
Also - I incorporated my comment about using a List rather than an array. I you don't like it just revert.
Joel Coehoorn
Changed the property to overridable- other than that, thanks for formatting it for me.
J Rothe
Actually, this is what I originally had but it gets messy. In the real project, the Order class has a list of Packages, and each Package has a list of OrderItems. Soon the generic arguments become a mile long because of the nesting of items. It also makes it so that you can never return a generic OrderBase with generic PackageBase and OrderItemBase.I guess I just don't understand why I'm really changing the signature when the return type is a subclass?
Casey
In that case why change the signature at all- hence my comment about ambiguity/abstraction (which i forgot so add :P). If you want to return the lowest common abstraction then the property should return an IList(of OrderItemBase). Leave generics out of it and the original signature alone.
J Rothe
I know there are some ways around this (including generics or interfaces), but my real question is why is this considered changing the signature when the return type is a subclass?
Casey
+1  A: 

You cannot change the signature of your class upon overriding it. You can, however, return a derived type:

Public Overrides Property OrderItem() as OrderItemBase
    Get
        Return New WebOrderItem()
    End Get
End Property

Public Sub Whatever()
    Dim item As WebOrderItem = DirectCast(OrderItem, WebOrderItem)
End Sub

Alternatively, if you want to enforce the types more strictly, use generics with generic type constraints, as shown below:

Public MustInherit Class OrderBase(Of T As OrderItemBase)
    Public MustOverride ReadOnly Property OrderItem() As T
End Class

Public Class OrderItemBase
End Class

Public Class WebOrder(Of T As WebOrderItem)
    Inherits OrderBase(Of T)

    Public Overrides ReadOnly Property OrderItem() As T
        Get
            Return New WebOrderItem()
        End Get
    End Property
End Class

Public Class WebOrderItem
    Inherits OrderItemBase
End Class

Or do this if you don't want WebOrder to be a generic class as well:

Public Class WebOrder
    Inherits OrderBase(Of WebOrderItem)

    Public Overrides ReadOnly Property OrderItem() As WebOrderItem
        Get
            Return New WebOrderItem()
        End Get
    End Property
End Class
Burg
A: 

One approach is to have a protected overridable method, and then have a public non-overridable method which calls the overridable one. Any time the return value for the function in the derived class should change, have a notoverridable override of the overridable method call a new overridable method which returns the more refined type, and also shadow the earlier version of the public function with one that uses the new override. If vb.net allowed one class to both override and shadow the same member, things would be much cleaner, but there's no way to do that.

Public Class CarFactory
  Protected Overridable Function DerivedMakeCar() as Car
    ... make a car
  End Function

  Public Function MakeCar() as Car
    Return DerivedMakeCar()
  End Function
...
End Class

Public Class FordFactory
  Inherits CarFactory

  Protected Overrides Function DerivedMakeCar() As Car
    Return DerivedMakeFord()
  End Function

  Protected Overridable Function DerivedMakeFord() As Ford
    ... Make a Ford
  End Function

  Public Shadows Function MakeCar() As Ford
    Return DerivedMakeFord()
  End FUnction
End Class

A simpler alternative in some cases may be to have a public overridable MakeCar() function which always returns an object of type Car, but have a FordFactory also include a MakeFord() function which returns a Ford. The overridden MakeCar() function would be NotOverridable and would simply call MakeFord. In some ways, the latter approach can be cleaner, but if there's a common naming convention (e.g. factories have a MakeProduct method which returns the most derived type) it may be useful to use Shadows.

supercat