0

As an example, I have a base class named Animal.

Public MustInherit Class Animal
    Public Property Name As String

      Public Sub New(animalName As String)
        Name = animalName
      End Sub

    Public Sub Sleep()
        MsgBox("ZZZZZZZZZZZ")
    End Sub

    Public MustOverride Sub MakeSound()

End Class

Subclasses can implement one or more of the following interfaces:

Public Interface IShed
    Sub Shed()
End Interface

Public Interface IBeAJerk
    Sub BeAJerk()
End Interface

I have 3 subclasses:

Public Class Dog
    Inherits Animal
    Implements IShed

    Public Sub New(dogName As String)
        MyBase.New(dogName)
    End Sub

    Public Overrides Sub MakeSound()
        MsgBox("Bark")
    End Sub

    Public Sub WagTail()
        MsgBox("Wag")
    End Sub

    Public Sub DogShed() Implements IShed.Shed
        MsgBox("Dog fur everywhere")
    End Sub

End Class

Public Class Cat
    Inherits Animal
    Implements IShed
    Implements IBeAJerk

    Public Sub New(catName As String)
        MyBase.New(catName)
    End Sub

    Public Overrides Sub MakeSound()
        MsgBox("Meow")
    End Sub

    Public Sub Purr()
        MsgBox("Purr")
    End Sub

    Public Sub CatShed() Implements IShed.Shed
        MsgBox("Cat fur everywhere")
    End Sub

    Public Sub Ignore() Implements IBeAJerk.BeAJerk
        MsgBox("Ignore owner")
    End Sub
End Class

Public Class Snake
    Inherits Animal
    Implements IBeAJerk

    Public Sub New(snakeName As String)
        MyBase.New(snakeName)
    End Sub

    Public Overrides Sub MakeSound()
        MsgBox("SSSSSS")
    End Sub

    Public Sub Slither()
        MsgBox("Slither")
    End Sub

    Public Sub Bite() Implements IBeAJerk.BeAJerk
        MsgBox("Bite owner")
    End Sub
End Class

Now I have a method that I want to accept an animal (a dog or a cat) that I know implements IShed. Is there a way to get to the interface without determining which type of animal it is? It would be nice to do something like:

Private Sub MakeAMess(sheddingAnimal As Animal.ThatImplementsIShed)
    'I want to be able to access both IShed (Shed method) and
    'Animal (Name property)
    sheddingAnimal.Shed()
    MsgBox(sheddingAnimal.Name & " made a mess!")
End Sub

I don't want to make the IShed interface into another abstract class because both the Cat and the Snake need to implement the IBeAJerk interface. But the Snake doesn't shed. (Well actually I guess snakes to shed their skin, but you get my point.)

Thanks for any help!

dwarn
  • 13
  • 3
  • @Igor Not quite, because you could choose to make a class that implements IShed, but does not inherit from Animal. The OP wants to guarantee _both_. – Joel Coehoorn Oct 18 '18 at 16:54
  • Generic constraints can do this, but be careful. When you do it that way, you end up with _one_ specific type in the method. For example, if you wanted to use a collection in the method, the `T` type parameter could be made to be both an Animal and IShed, but ultimately collection is for either a specific Cat or Dog type, and not a new type that can be both. – Joel Coehoorn Oct 18 '18 at 16:58
  • @Igor It's not clear from the example, but I infer this via the prose and thrust of the question. – Joel Coehoorn Oct 18 '18 at 16:58
  • You cannot express "a subclass of X that implements Y" with regular parameters. You need [generic constraints](https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/data-types/generic-types#constraints) for that, or detect interface support at runtime. – GSerg Oct 18 '18 at 16:59
  • Thanks! My example was not clear, but I want to be able to access both IShed and Animal in my MakeAMess method. I've updated the question to reflect that. – dwarn Oct 18 '18 at 17:29

1 Answers1

1

I would recommend creating a new interface IAnimal and then have IShed extend that interface (same goes for IBeAJerk). Type Animal would then implement IAnimal. The method would then take a parameter of type IShed.

Public Interface IAnimal
  Property Name As String
  Sub New(animalName As String)
  Sub Sleep()
  Sub MakeSound()
End Interface

Public Interface IShed
    Inherits IAnimal
    Sub Shed()
End Interface

Public Interface IBeAJerk
    Inherits IAnimal
    Sub BeAJerk()
End Interface

Alternatively you could allow Animal as the parameter to the method and check at runtime if the passed in instance implements IShed and then call Shed

Private Sub MakeAMess(animal As Animal) 
  (TryCast(animal, IShed))?.Shed() 
  ' additional code doing stuff with Animal
End Sub
Igor
  • 60,821
  • 10
  • 100
  • 175