thexebolud thexebolud - 6 months ago 12
Vb.net Question

Can I have a function with return type of the class itself in inherited classes? VB.NET

I have a parent class that is also a factory. For example:

Public Class Factory
Public Function clone() as Factory
' Some logic here
' return something
End Function

Public Function all() as List (Of Factory)
' Some logic here
' return something
End Function
End Class


And then an inherited one

Public Class BookFactory
inherits Factory
End Class


I can use inflection in the Factory class to generate the proper extended objects when called by the inherited one.
myBookFactory.clone()
will then return a
BookFactory
instance and not only a
Factory
instance.

The problem: this
BookFactory
instance will be cast as
Factory
, since the type of the function is
Factory
and not
BookFactory
.

I'd like to do something like

Public Class Factory
Public Function clone() as Me.GetType()
' Some logic here
' return something
End Function

Public Function all() as List (Of Me.GetType())
' Some logic here
' return something
End Function
End Class


So the returned value would be correctly cast and avoid having to do this each time:

Dim myBookFactory2 = DirectCast(myBookFactory1.clone(), myBookFactory1.getType())


How can I do this?

Answer

This seems to be a variation on asking for covariant return types. As you have noticed, this is not supported by VB.NET (or C# for that matter). Typically this is asked in the context of overriding virtual methods, where it is still not allowed. There are several alternatives, each with their own pros and cons.

Use a generic template argument to specify the derived class

This is similar to the way IComparable<T> is most commonly implemented.

Public Class Factory(Of T As Factory)
    Public Function Clone() As T
        'use GetType(T) to determine derived type
    End Function
End Class

Public Class BookFactory
    Inherits Factory(Of BookFactory)
End Class

Additionally, if you can add a New constraint to the Factory (eg: Factory(Of T {New, Factory(Of T)})) base class, you may be able to avoid using reflection.

However, this does not prevent the accidental (or potentially malicious) mistake of declaring a class like this:

Public Class EvilFactory
    Inherits Factory(Of BookFactory)
    'hmmm, now clone will be making the wrong type
End Class

Also, this approach makes it impossible to create a list of factories of different types without resorting to another base class below Factory(Of T) or declaring the list as being of object.

Make new methods on the derived classes that return the specific type you want.

Public Class Factory
    Public Function Clone() As Factory
        'create derived class, but return as base
    End Function
End Class

Public Class BookFactory
    Inherits Factory

    Public Function CloneBooks() As BookFactory
        Return CType(Me.Clone(), BookFactory)
    End Function
End Class

This allows you to hide the cast for those times when you know you have a BookFactory and want to get another BookFactory. It also lets you treat all factory types polymorphically in the normal inheritance sense. However, if you have an object typed as Factory, you will still get back an object type as Factory.

Reconsider the inheritance relationship

Depending on how these classes are used, it may not make sense to use the inheritance relationship here. If you are only concerned with not retyping code, you may want to look into code generation instead.

Comments