-->

VB.NET Forced Inheritance through multiple generat

2020-07-24 04:05发布

问题:

I'm trying to wrap my head around inheritance/interfaces/implementation a bit better in .NET.

I have a class that's defined as follows (sort of):

Public Class Sheet
    Property Name As String
    Property Steps As List(Of [Step])
End Class

The thing is, [Step] is just a virtual, base class. There are 5 different concrete implementations of [Step]. To make matters a bit more complex, there are 3 DIRECT implementations of [Step], 2 of which are virtual. Each of those 2 has 2 subclasses that would concretely implement [Step].

So, here's how it looks:

                          Step
         |-----------------|-----------------|
         |                 |                 |
     SubStepA          SubStepB          SubStepC
    |----|----|                         |----|----|
    |         |                         |         |
SubStepAA SubStepAB                 SubStepCA SubStepCB

So, SubStepB, SubStepAA, SubStepAB, SubStepCA and SubStepCB are the concrete implementations.

There are a couple of things that I'd like ANY Step to do, such as Clone().

So, I tried declaring the following in Step:

Public MustOverride Function Clone() As Step

The problem is that, when I attempt to implement that in SubStepAA, I can't declare the following:

Public Overrides Function Clone() As SubStepAA

If I do that, I get an error that the return types aren't the same.

Is the solution to this to just use a DirectCast call anytime I clone a concrete subclass? That seems odd and unsatisfying. It also just seems wrong. I mean, if I clone a SubStepAA object, I want to get back an object of type SubStepAA.

There's got to be a way to do this, right? I mean, I guess I could just declare each class the way it needs to be, but it also seems wrong to have to write 5 DIFFERENT Clone() methods that just HAPPEN to work in (essentially) the same way (creating a deep copy of the referenced object).

I've looked into using Interface declarations, but that seems to suffer from the same type mismatch error.

Please tell me that I'm just missing something basic!

Thanks!

As an aside, I have been doing some reading and I realize that there may be more optimized ways to do deep copies of object (e.g., through serialization/deserialization), but I'm still interested in this question, even if I choose to clone objects using a different approach.

回答1:

This may not be exactly what you are hoping for, but you can meet all your requirements by using a generic base type, like this:

Public MustInherit Class [Step](Of T)
    Public MustOverride Function Clone() As T
End Class

Public Class StepA
    Inherits [Step](Of StepA)

    Public Overrides Function Clone() As StepA
        ' ...
    End Function
End Class

However, then, there would be no common Step base class that would be usable for all the derived types. For instance, there would be no way to do something like this:

Dim s As [Step] = New StepA()  'Won't work because there is no Step type, only a generic Step(T) type
Dim c As [Step] = s.Clone()

However, if you need to have a common base type like that, you could still do something like that, albeit with some additional complication:

Public Interface ICloneable(Of T)
    Function Clone() As T
End Interface

Public MustInherit Class [Step]
    Implements ICloneable(Of [Step])

    Public MustOverride Function CloneBase() As [Step] Implements ICloneable(Of [Step]).Clone
End Class

Public MustInherit Class [Step](Of T As [Step])
    Inherits [Step]

    Implements ICloneable(Of T)

    Public Overrides Function CloneBase() As [Step]
        Return Clone()
    End Function

    Public MustOverride Function Clone() As T Implements ICloneable(Of T).Clone
End Class

Public Class StepA
    Inherits [Step](Of StepA)

    Public Overrides Function Clone() As StepA
        ' ...
    End Function
End Class

If you did it that way, you would have that additional layer of abstraction where you could cast each concrete object as either a Step(T) or as a Step. For instance, you could then do this:

Dim s As [Step] = New StepA()
Dim c As [Step] = s.CloneBase()

But of course, this all begs the question, is it worth all this complication? The two simpler solutions would be to implement the interface independently on each derived class (and thereby forgo the ability to call clone from the base class), or else go with your first idea and just have the Clone method always return the base type.