1

I have a following hierarchy (I don't write here constructors and similar items for brevity):

Interface IData
    ReadOnly Property ID As Integer
    ReadOnly Property Name As String
End Interface

Public Class Data1
    Implements IData
    ReadOnly Property CoolProperty1 As String
End Class

Public Class Data2
    Implements IData
    ReadOnly Property CoolProperty2 As String
End Class

Now, the code in WinForms app is:

Dim a As List(Of IData) = {New Data1(1, "Name1", "Cool1_1"), 
                           New Data1(1, "Name2", "Cool1_2")}.ToList();
someDataGridView.DataSource = a

When I do it, I only have columns ID and Name. In order to see column CoolProperty1, I have to do explicit Cast:

Sub FillDGV(ByVal lst as List(Of IData)
   someDataGridView.DataSource = a.Cast(Of Data1).ToList()
End Sub

And if I account for class Data2, the Sub above becomes:

Sub FillDGV(ByVal lst as List(Of IData)
   If Not lst.Any Then Return
   If TypeOf lst.First Is Data1 Then
       someDataGridView.DataSource = lst.Cast(Of Data1).ToList()
   ElseIf TypeOf lst.First Is Data2 Then
       someDataGridView.DataSource = lst.Cast(Of Data2).ToList()
   Else
       someDataGridView.DataSource = lst
   EndIf
End Sub

I do understand why compiler does it like this, but I wonder if there's any nice pattern which would allow me to avoid such casts.

UPDATE: The thing is that my FillDGV function always receives list, which contains items of the same type, it is either List(Of Data1) or List(Of Data2).

SOLUTION: It seems I have an idea. Generics would help here:

Sub FillDGV(Of T As IData)(ByVal lst as List(Of T))
   someDataGridView.DataSource = lst
End Sub
Rustam
  • 1,766
  • 18
  • 34
  • you want to show all the Properties? – King King Jun 17 '13 at 07:56
  • 1
    Add `CoolProperty` to `IData` and implement it differently in `Data1` and `Data2`? - That's pretty much what interfaces are for. – Jon Egerton Jun 17 '13 at 08:00
  • King King, I want to show all accessible properties; – Rustam Jun 17 '13 at 08:17
  • Jon Egerton, I need two distinct properties, I could have better called them CoolProperty and SomeCompletelyAnotherProperty. They have nothing in common and that's why I cant put them into interface – Rustam Jun 17 '13 at 08:18
  • @GusRustam about your update, your `Data1` and `Data2` are two totally different types, so your grid DataSource is not always of the same type. That means you still have to use an `If` statement to cast the DataSource and show the Properties accordingly. However, I think all those classes implement the interface `IData` so both should have all the Properties members of that interface, I don't think the last `else` is needed. – King King Jun 17 '13 at 08:49
  • I suspect that because your list is of type IData it'll only expose the 2 properties unless you cast it to the classes. Since as you've mentioned that the 2 extra properties are unrelated you should probably have 2 lists one for each class implementing IData. Rather than one function trying to decide which list to show, you meight want to consider overloading it and having a separate on for each list. – tinstaafl Jun 17 '13 at 08:56
  • @tinstaafl Thanks, its a good idea, overloading is anyway better than if-else – Rustam Jun 17 '13 at 10:45
  • @KingKing Why do you say Data1 and Data2 are completely different? They share ID and Name fields from interface... – Rustam Jun 17 '13 at 10:47
  • @GusRustam they have at least 1 Property different from the other so we can say they're totally different. You can't cast one of them to the other. – King King Jun 17 '13 at 10:55

2 Answers2

0

You either need to have a FillDGV sub for each type, or one generic sub that covers all types that implement the interface. If you are doing the same thing with each type (i.e., just binding it to a grid) then you can go the generic route. If you want to do something specific to each type, then you need to overload the FillDGV sub like so:

Sub FillDGV(ByVal lst as List(Of Data1))
   If Not lst.Any Then Return
   For each item In lst
      Console.WriteLine(item.CoolProperty1.ToString())
   Next item
   someDataGridView.DataSource = lst
End Sub

Sub FillDGV(ByVal lst as List(Of Data2))
   If Not lst.Any Then Return
   For each item In lst
      Console.WriteLine(item.CoolProperty2.ToString())
   Next item
   someDataGridView.DataSource = lst
End Sub
Douglas Barbin
  • 3,595
  • 2
  • 14
  • 34
0

(Dublicating answer above).

It seems I have an idea. Generics would help here:

Sub FillDGV(Of T As IData)(ByVal lst as List(Of T))
   someDataGridView.DataSource = a
End Sub
Rustam
  • 1,766
  • 18
  • 34