Best way to call inherited method with var of base class?

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

I have a set of classes:

C (base)
C1 (inherits C)
C2 (inherits C)
...
Cn (inherits C)

(the C1..Cn also have subclasses, but thats not really relevant to the
question)

C has the following functions:

F(C) (implemented in C)
F(C1) (mustoverride)
...
F(Cn) (mustoverride)

When I declare two variables (x, y) of type C, and call x.F(y), it calls the
base class implementation of F(C) which is quite understandable. However, I
would like to actually call the appropriate F(Cn) based on the actual classes
of x & y.

What is the best way of doing this?

Currently I have a case statement inside F(C), which is (a) bad for
maintenance and (b) a kludge.
 
Philip Warner said:
I have a set of classes:

C (base)
C1 (inherits C)
C2 (inherits C)
...
Cn (inherits C)

(the C1..Cn also have subclasses, but thats not really relevant to the
question)

C has the following functions:

F(C) (implemented in C)
F(C1) (mustoverride)
...
F(Cn) (mustoverride)

When I declare two variables (x, y) of type C, and call
x.F(y), it calls the base class implementation of F(C) which
is quite understandable. However, I would like to actually
call the appropriate F(Cn) based on the actual classes
of x & y.

http://groups.google.de/[email protected]
 
Herfried K. Wagner said:

I think you *may* have missed the point; I am not trying to call the base
class methods directly, I asm trying to ensure that the narrowest possible
function signature is used.

I can understand why VB needs to obey the class of the variable
declarations, but the question is: if I *want* to call the narrower function
based on the class of the parameters, what is the best way to do it?

In my specific example, I only have one level of inheritance, and there is
no code in the base class.
 
Philip.
When I declare two variables (x, y) of type C, and call x.F(y), it calls
the
base class implementation of F(C) which is quite understandable. However,
I
would like to actually call the appropriate F(Cn) based on the actual
classes
of x & y.
It sounds like you want a double dispatch. Try something like:

Public MustInherit Class C

Public MustOverride Sub F(ByVal aC As C)
Public MustOverride Sub F(ByVal aC1 As C1)
Public MustOverride Sub F(ByVal aC2 As C2)
Public MustOverride Sub F(ByVal aC3 As C3)

End Class

Public Class C1
Inherits C

Public Overloads Overrides Sub F(ByVal aC As C)
' Me is now C1, so C.F(C1) is called
aC.F(Me)
End Sub

Public Overloads Overrides Sub F(ByVal aC1 As C1)
Debug.WriteLine("F(c1)", "C1")
End Sub

Public Overloads Overrides Sub F(ByVal aC2 As C2)
Debug.WriteLine("F(c2)", "C1")
End Sub

Public Overloads Overrides Sub F(ByVal aC3 As C3)
Debug.WriteLine("F(c3)", "C1")
End Sub

End Class

Public Class C2
Inherits C

Public Overloads Overrides Sub F(ByVal aC As C)
' Me is now C2, so C.F(C2) is called
aC.F(Me)
End Sub

Public Overloads Overrides Sub F(ByVal aC1 As C1)
Debug.WriteLine("F(c1)", "C2")
End Sub

Public Overloads Overrides Sub F(ByVal aC2 As C2)
Debug.WriteLine("F(c2)", "C2")
End Sub

Public Overloads Overrides Sub F(ByVal aC3 As C3)
Debug.WriteLine("F(c3)", "C2")
End Sub

End Class

Public Class C3
Inherits C

Public Overloads Overrides Sub F(ByVal aC As C)
' Me is now C3, so C.F(C3) is called
aC.F(Me)
End Sub

Public Overloads Overrides Sub F(ByVal aC1 As C1)
Debug.WriteLine("F(c1)", "C3")
End Sub

Public Overloads Overrides Sub F(ByVal aC2 As C2)
Debug.WriteLine("F(c2)", "C3")
End Sub

Public Overloads Overrides Sub F(ByVal aC3 As C3)
Debug.WriteLine("F(c3)", "C3")
End Sub

End Class


FWIW: Rather then overloading function F, I normally give each a distinct
name, something like:

Public MustInherit Class C

Public MustOverride Sub F(ByVal aC As C)
Public MustOverride Sub FC1(ByVal aC1 As C1)
Public MustOverride Sub FC2(ByVal aC2 As C2)
Public MustOverride Sub FC3(ByVal aC3 As C3)

End Class

If don't want the "reversal" of the parameters you can make F a template
method. Something like:

Public MustInherit Class C

Public Sub F(ByVal aC As C)
aC.FC(Me)
End Sub

Public MustOverride Sub FC(ByVal aC As C)
Public MustOverride Sub FC1(ByVal aC1 As C1)
Public MustOverride Sub FC2(ByVal aC2 As C2)
Public MustOverride Sub FC3(ByVal aC3 As C3)

End Class

Hope this helps
Jay
 
It sounds like you want a double dispatch.

Thanks for this; it's very long-winded but it will achieve the result.

I don't suppose you know of any way I can do it using Reflection and the
Type object etc? It would be nice to avoid writing two functions for every
one I need.
 
Philip,
I'm sure you could kludge together something with Reflection. However I have
to ask why?

Using Reflection I'm certain would be more fragile, and perform
significantly worse (as late binding or Reflection pretty much always
perform worse), and possibly be harder to follow. Granted double dispatch is
a little more advance then normal overridable functions, however not that
much more as you are simply having an overridable function call an
overridable function.

Also I don't think Reflection you are really going to save on the number of
functions you need (see below).

Try single stepping the following version to see how the double dispatch
works.

---x--- cut here ---x---
Public MustInherit Class C

Public Sub F(ByVal aC As C)
aC.Fc(Me)
End Sub

Public MustOverride Sub Fc(ByVal aC As C)

' NOTE we use qualified names rather then overloading here
' we could have just as easily call these all Fc...
Public MustOverride Sub Fc1(ByVal aC1 As C1)
Public MustOverride Sub Fc2(ByVal aC2 As C2)
Public MustOverride Sub Fc3(ByVal aC3 As C3)
Public MustOverride Sub Fc4(ByVal aC4 As C4)

End Class

Public Class C1
Inherits C

Public Overrides Sub Fc(ByVal aC As C)
aC.Fc1(Me)
End Sub

Public Overrides Sub Fc1(ByVal aC1 As C1)
Debug.WriteLine("Fc1(c1)", "c1")
End Sub

Public Overrides Sub Fc2(ByVal aC2 As C2)
Debug.WriteLine("Fc2(c2)", "c1")
End Sub

Public Overrides Sub Fc3(ByVal aC3 As C3)
Debug.WriteLine("Fc3(c3)", "c1")
End Sub

Public Overrides Sub Fc4(ByVal aC4 As C4)
Debug.WriteLine("Fc4(c4)", "c1")
End Sub

End Class

Public Class C2
Inherits C

Public Overrides Sub Fc(ByVal aC As C)
aC.Fc2(Me)
End Sub

Public Overrides Sub Fc1(ByVal aC1 As C1)
Debug.WriteLine("Fc1(c1)", "c2")
End Sub

Public Overrides Sub Fc2(ByVal aC2 As C2)
Debug.WriteLine("Fc2(c2)", "c2")
End Sub

Public Overrides Sub Fc3(ByVal aC3 As C3)
Debug.WriteLine("Fc3(c3)", "c2")
End Sub

Public Overrides Sub Fc4(ByVal aC4 As C4)
Debug.WriteLine("Fc4(c4)", "c2")
End Sub

End Class

Public Class C3
Inherits C

Public Overrides Sub Fc(ByVal aC As C)
aC.Fc3(Me)
End Sub

Public Overrides Sub Fc1(ByVal aC1 As C1)
Debug.WriteLine("Fc1(c1)", "c3")
End Sub

Public Overrides Sub Fc2(ByVal aC2 As C2)
Debug.WriteLine("Fc2(c2)", "c3")
End Sub

Public Overrides Sub Fc3(ByVal aC3 As C3)
Debug.WriteLine("Fc3(c3)", "c3")
End Sub

Public Overrides Sub Fc4(ByVal aC4 As C4)
Debug.WriteLine("Fc4(c4)", "c3")
End Sub

End Class

Public Class C4
Inherits C

Public Overrides Sub Fc(ByVal aC As C)
aC.Fc4(Me)
End Sub

Public Overrides Sub Fc1(ByVal aC1 As C1)
Debug.WriteLine("Fc1(c1)", "c4")
End Sub

Public Overrides Sub Fc2(ByVal aC2 As C2)
Debug.WriteLine("Fc2(c2)", "c4")
End Sub

Public Overrides Sub Fc3(ByVal aC3 As C3)
Debug.WriteLine("Fc3(c3)", "c4")
End Sub

Public Overrides Sub Fc4(ByVal aC4 As C4)
Debug.WriteLine("Fc4(c4)", "c4")
End Sub

End Class

Public Class Test

Public Shared Sub Main()
Dim xlist() As C = {New C1, New C2, New C3, New C4}
Dim ylist() As C = {New C1, New C2, New C3, New C4}

For Each x As C In xlist
For Each y As C In ylist
Debug.WriteLine(x, "x")
Debug.WriteLine(y, "y")
x.F(y)
Debug.WriteLine(Nothing)
Next
Next
End Sub

End Class

---x--- cut here ---x---

If you are really not following the double dispatch I would recommend you
use a select case as you originally suggested and avoid reflection. As the
select case will be easier to understand and perform better then using
Reflection.

However! I recommend Double Dispatch as it is a rather common OO method to
solve the problem you seem to be stating. For example: The Visitor Pattern
is commonly implemented as Double Dispatch in single inheritance languages
such as VB.NET & C#.
I don't suppose you know of any way I can do it using Reflection and the
Type object etc? It would be nice to avoid writing two functions for every
one I need.
Why do you think there are twice as many as you need?

You need (N+1)^2 functions, you write (N+1)^2 functions. Your original post
suggested class C1 to CN need functions F(C1) to F(CN), the double dispatch
code has F(C1) to F(CN) + F(C) in each class, which for an number of N > 1
does not add up to "twice as many"...

In other words with Double Dispatch, if you have N classes you need N
functions per class, plus you need 1 extra function per class that actually
does the double dispatch.

Your original post suggested if you have N classes you would have N
functions per class (as all the functions were MustOverride). Double
dispatch is simply adding +1 to each class.

In both cases you have an abstact base class, simply to define what the
above classes need to look like. Of course if some combinations of F(C1) to
F(CN) are not logical you could always leave them in the base class as
simply Overridable & provide "default" implementation for all the derived
classes...

Unfortunately I am not seeing any really good links on double dispatch.

Hope this helps
Jay
 
I'm sure you could kludge together something with Reflection. However I have
to ask why?
Ignorance?


Using Reflection I'm certain would be more fragile, and perform
significantly worse

Then I'll avoid it.

However! I recommend Double Dispatch as it is a rather common OO method to
solve the problem you seem to be stating.

It what I have currently (badly) implemented (after I gave up on the case
statement); I made the mistake of thinking I needed to implement n Fc's,
rather than 1 Fc and n Fc1's...if that makes sense. Much happier now.

Why do you think there are twice as many as you need?

In this case, fuzzy thinking...it was late at night...the dog ate my homework.

Unfortunately I am not seeing any really good links on double dispatch.

No problem; I'll use double-dispatching, especially now that I have an
official-sounding name for it.

Thanks for you extremely helpful replies.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top