Search code examples
vb.netinterfacecastingsubtype

Cannot pass List of subtype to a List of interface type (implemented by subtype) to a function in Visual Basic + Cannot be casted either


I need to use only 1 function to process a list containing interface types so that I can reuse it for each subtype, but I also need to know the concrete type when handling the list-items higher up the function call stack:

LoadAItems calls GetA which calls FilterItems.

FilterItems needs to be generic so that GetB can also call and make use of it.

The problem is that trying to pass the list of subtypes to the general FilterItems method is not allowed:

"Cannot convert type 'List(Of AListItem)' to parameter type 'List(Of IListItem)'"

I tried converting each AListItem object in the list to IListItem and adding it to a new list but the problem is that the FilterItems function is supposed to remove elements from the list. If I remove elements from the new list then it will not affect the old list. I could convert it back but this is a lot of hassle just to be able to use the function.

I cannot just change everything to List(Of IListItem) because then I would need to always cast down the returned value from either FilterItems or GetA / GetB because LoadAItems / LoadBItems needs to know the concrete type.

I can see why casting down is bad, but why can't I cast up to the interface type the concrete types are implementing?

I have already tried:

FilterItems(CType(items, List(Of IListItem))

but this is not allowed:

"Value of type 'List(Of AListItem)' cannot be converted to 'List(Of IListItem)'"

Here is my code example:

Public Class AListItem
    Implements IListItem

    'Properties here

End Class

Public Class BListItem
    Implements IListItem

    'Properties here

End Class

Private Sub FilterItems(items As List(Of IListItem))
    'Remove items from the list that meet some condition
    items.RemoveAll(Function(item) ...)     

    'Does not matter what the items class type is
End Sub

Public Function GetA() As List(Of AListItem)
    Dim items As List(Of AListItem)

    items = CallDatabase()

    FilterItems(items) ' Does not allow!

    Return items
End Function

Public Function GetB() As List(Of BListItem)
    Dim items As List(Of BListItem)

    items = CallDatabase()

    FilterItems(items) ' Does not allow!

    Return items

End Function

Public Sub LoadAItems()
    Dim items As List(Of AListItem)

    items = GetA()

    'Do specific AListItem stuff (cannot use interface!)
End Sub

Public Sub LoadBItems()
    Dim items As List(Of BListItem)

    items = GetB()

    'Do specific BListItem stuff (cannot use interface!)
End Sub

Solution

  • If I correctly understand what you're trying to do you should be able to make the function itself generic:

    Private Sub FilterItems(Of T As IListItem)(items As List(Of T))
    

    Of T As IListItem adds a constraint that T must be, or inherit from IListItem.

    Then you can call it like:

    Public Function GetA() As List(Of AListItem)
        Dim items As List(Of AListItem)
    
        items = CallDatabase()
    
        FilterItems(Of AListItem)(items)
    
        Return items
    End Function
    
    Public Function GetB() As List(Of BListItem)
        Dim items As List(Of BListItem)
    
        items = CallDatabase()
    
        FilterItems(Of BListItem)(items)
    
        Return items
    End Function