2

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.

mbm29414
  • 11,558
  • 6
  • 56
  • 87

1 Answers1

2

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.

Steven Doggart
  • 43,358
  • 8
  • 68
  • 105
  • Can you explain the second part? It looks more like what I think I'd like to use, but when I do the "Public MustInherit Class [Step](Of T As [Step])", I get a "too few type arguments" error. And it appears to be looking for another "[Step]" class. Thoughts? – mbm29414 Dec 28 '12 at 16:41
  • HOLY COW, @Steven... I've got to read this at least 10 more times before even IMAGINING I understand what you're saying... Thanks for such an awesome answer!! +1!!!!! – John Bustos Dec 28 '12 at 16:43
  • @mbm30075 What version of Visual Studio are you using? I just copy and pasted it into VS 2005 and it worked perfectly fine with no changes. Generics were not available in versions prior to that, though. – Steven Doggart Dec 28 '12 at 16:45
  • Visual Studio 2012. What I'm asking, though, is whether the "bottom" section stands alone. It doesn't appear to do so in my VS. I'm thinking that the top is maybe more what I want, but I don't get the "intermediate" class "Class(Of T As Class)". I've never seen that syntax before. What is it? What does it mean? – mbm29414 Dec 28 '12 at 16:46
  • For this particular implementation, it's fine to not have a USEABLE base class. Just want an abstract base class. – mbm29414 Dec 28 '12 at 16:48
  • OK, so when I copy/paste the top section of code, I get the following error: "Too few type arguments to 'InheritanceTesting.Step(Of T)'." – mbm29414 Dec 28 '12 at 16:51
  • Ah, Ok. I see the problem. We were talking about different sets of code. That was a copy/paste error. I fixed it. Sorry about that. – Steven Doggart Dec 28 '12 at 17:36
  • So, I now understand the code in the top section, but I can't figure out a way to have the interim abstract class. Is that impossible? I'm currently working with just an Interface and implementing that in my concrete classes. Is that the way I should go? – mbm29414 Dec 28 '12 at 18:23
  • Do you need to be able to call the Clone method using a variable of the base type? If so, that's what my second example demonstrates. If not, just create a base Step class which implements everything except Clone, then inherit that into the generic version of Step which adds the Clone method, then inherit the generic version of Step into all the concrete classes. – Steven Doggart Dec 28 '12 at 19:49
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/21854/discussion-between-mbm30075-and-steven-doggart) – mbm29414 Dec 29 '12 at 01:25