VB Generics "cannot be converted" - vb.net

For the problem I have these classes:
Public MustInherit Class BaseLeaf(Of T)
Implements IBaseLeaf
// etc
Public Class WebsiteLeaf
Inherits BaseLeaf(Of Headline)
// etc
Public Class WebsiteCollection
Inherits BaseCollection(Of WebsiteLeaf)
// etc
Public Class SubscriptionList
Private mCollection As BaseCollection(Of IBaseLeaf)
Public Sub LoadSubscriptions(ByVal collection As BaseCollection(Of IBaseLeaf))
mCollection = collection
End Sub
In the Main class I am trying to call the following function:
Private Sub FetchSubscriptions(ByVal websites As WebsiteCollection)
gUser.SubscriptionList.LoadSubscriptions(websites)
// code
End Sub
This will access LoadSubscriptions with passing the "websites" variable.
As you can see LoadSubscriptions expects a BaseCollection(Of IBaseLeaf).
The "websites" variable is a WebsiteCollection, which is a BaseCollection(Of WebsiteLeaf(Of Headline)).
Now I am getting the error: Value of type 'WebsiteCollection' cannot be converted to 'BaseCollection(Of IBaseLeaf)'.
What I am doing wrong here?

If class B is a descendent of class A this does not mean that Collection(Of B) is a descendant of Collection(Of A)!
If you have these definitions
Dim stringList As List(Of String)
Public Sub DoSomthingWithList(list As List(Of object))
list.Add(Date.Now)
End Sub
and you could call the method like this
DoSomthingWithList(stringList)
then the method would try to add a Date to the list which is actually a list of strings! Since the parameter is typed as object the date would be boxed (i.e. converted to an object) but not converted to string.
Therefore the collections List(Of X) and List(Of Y) are never compatible, even if Y inherits X.
Let's look at another example
Public Class TestClass
Implements IBaseLeaf
// etc
What happens if you call LoadSubscriptions with a WebsiteCollection?
Public Sub LoadSubscriptions(ByVal collection As BaseCollection(Of IBaseLeaf))
collection.Add(New TestClass())
End Sub

Related

VB.NET Trigger method from within a generic-list property when values change by using Add/Addrange/Remove, etc

How to call a method from within a property which is declared as a List(Of T), when new values were added into Property?
In my example you see a class called Something, another class called MyClass with a Property Things As List(Of Something), and my main form where a new instance of MyClassis made, then some values has been added into its' Things Property by using the Add (or AddRange) method of generic lists.
My question: how to trigger DoSomeAction() method every time Things property values change (added or removed by using Add, Addrange, Remove, etc. methods)?
Public Partial Class MainForm ' System.Windows.Forms.Form
Dim newMyClass As New MyClass
newMyClass.Things.Add(New Something())
newMyClass.Things.AddRange(New Something() {New Something(), New Something()})
End Class
Public Class MyClass
Private thingsValue As List(Of Something)
'
Public Property Things() As List(Of Something)
Set(value As List(Of Something))
thingsValue = value
DoSomeAction()
End Set
Get
Return thingsValue
End Get
End Property
'
Private Sub DoSomeAction()
'[inside method DoSomeAction()]
End Sub
'
Public Sub New()
thingsValue = New List(Of Something)
End Sub
End Class
Public Class Something
'[inside class Something]
End Class

how to set constructor with List(of Class) parameter

Given the scenario where I am building a wrapper for the data I pass to jquery datatables and I have for example, three Classes Orders, OrderDetails and Customers. In datatables, lists of objects are denoted by the variable name aoData, so to construct that I have to pass a List(Of someclass). Since I don't know which class until I pass it how do you do it? is There a GetType that takes the class name as a string? I couldn't find one.
Public Class DataTablesWrapper
Public Sub New(ByRef data As List(Of String))
Me.aaData = data
End Sub
Public Sub New(ByRef data As List(Of some class depending on what data I want wrapped))
Me.aoData = data
End Sub
Public Property aaData As List(Of String)
Public Property aoData As List(Of some class depending on what data I want wrapped)
End Class
I would recommend the use of generics.
In your case it would look like this:
Public Class DataTablesWrapper(Of T)
Public Sub New(ByRef data As List(Of T))
Me.aaData = data
End Sub
Public Property aaData As List(Of T)
End Class
Creating your instance would then look like this:
Sub Main()
Dim stringList As New List(Of String)({"foo", "bar"})
Dim tablesWrapper As New DataTablesWrapper(Of String)(stringList)
End Sub
You don't want to use List(Of Object) due to this reasons.

What happens if a base method calls an overridden method in VB?

Consider the following example:
Public Class ParentClass
Public Sub GenerateReport
Dim Col As Collection
Col = GetItemCollection()
End Sub
Public Overridable Function GetItemCollection() As Collection
GetItemCollection = New Collection
GetItemCollection.Add("1")
GetItemCollection.Add("2")
GetItemCollection.Add("3")
End Function
End Class
Public Class ExtendedClass
Inherits ParentClass
Public Overrides Function GetItemCollection() As Collection
GetItemCollection = New Collection
GetItemCollection.Add("A")
GetItemCollection.Add("B")
GetItemCollection.Add("C")
End Function
End Class
Public Sub Main()
Dim cls As New ExtendedClass
cls.GenerateReport()
End Sub
When Main() calls cls.GenerateReport(), is the variable Col going to be a collection of numbers or letters? I'm hoping that it will recognize that cls is an instance of ExtendedClass and call the overridden method and return the letters.
It will be a collection of letters as you did override the method. However, where did you declare the GetItemCollection? You still need an instance variable.

Similar classes with different signatures

I have two classes:
Public Class Subscribing
Private _subscribingObjects As IList(Of String)
Public Sub Add(ByVal obj As SubscribeObject)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Public Class Providing
Private _providingObjects As IList(Of String)
Public Sub Add(ByVal obj As ProvideObject)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Is there a more elegant way to add do this? One class would suffice, but since the Add methods have different arguments, then one really wouldn't work.
Any help would be appreciated.
this?
Public Class SubscribingProviding(Of t)
Private _subscribingObjects As IList(Of String)
Public Sub Add(ByVal obj As t)
'...code...'
End Sub
Public Sub Remove(ByVal index As Integer)
'...code...'
End Sub
End Class
Your add functions should be fine. As long as you have different variable types being passed in you can have the function names be the same. Your remove Subs will not be allowed in the same class because it is using the same parameter Integer.
Eh.. probably not. They are different enough that you cant even Interface them.
I personally wouldn't mix the two responsibilities (of subscribing and providing) in one class. The classes themselves can easily be simplified by just inheriting from List(Of T)
Public Class Subscribing
Inherits List(Of SubscribeObject)
End Class
Public Class Providing
Inherits List(Of ProvideObject)
End Class
If you really want to get down to one class and make sure that it can only accept SubscribeObject and ProvideObject respectively, implement a common interface in both SubscribeObject and ProvideObject. Then create a generic class that accepts the interface:
' Common interface '
Public Interface ISubscribeProvideObject
End Interface
' SubscribeObject and ProvideObject both implementing the common interface '
Public Class SubscribeObject
Implements ISubscribeProvideObject
'...'
End Class
Public Class ProvideObject
Implements ISubscribeProvideObject
'...'
End Class
' Generic class accepting both types '
Public Class SubscribingProviding(Of T As ISubscribeProvideObject)
Inherits List(Of T)
'... Add() and Remove() methods only needed if custom logic applies ...'
End Class

Can I inherit from a generic class without specifying a type?

I have the following sample code in a VB.NET console application. It compiles and works, but feels like a hack. Is there a way to define EmptyChild so that it inherits from Intermediate(Of T As Class) without using the dummy EmptyClass?
Module Module1
Sub Main()
Dim Child1 = New RealChild()
Child1.Content = New RealClass()
Dim Child2 = New EmptyChild()
Console.WriteLine("RealChild says: " & Child1.Test)
Console.WriteLine("EmptyChild says: " & Child2.Test)
Console.ReadLine()
End Sub
Public Class EmptyClass
End Class
Public Class RealClass
Public Overrides Function ToString() As String
Return "This is the RealClass"
End Function
End Class
Public MustInherit Class Base(Of T As Class)
Private _content As T = Nothing
Public Property Content() As T
Get
Return _content
End Get
Set(ByVal value As T)
_content = value
End Set
End Property
Public Overridable Function Test() As String
If Me._content IsNot Nothing Then
Return Me._content.ToString
Else
Return "Content not initialized."
End If
End Function
End Class
Public MustInherit Class Intermediate(Of T As Class)
Inherits Base(Of T)
'some methods/properties here needed by Child classes
End Class
Public Class RealChild
Inherits Intermediate(Of RealClass)
'This class needs all functionality from Intermediate.
End Class
Public Class EmptyChild
Inherits Intermediate(Of EmptyClass)
'This class needs some functionality from Intermediate,
' but not the Content as T property.
Public Overrides Function Test() As String
Return "We don't care about Content property or Type T here."
End Function
End Class
End Module
The other way to do this would be to move the generic code out of the Base class and then create 2 Intermediate classes like this:
Public MustInherit Class Intermediate
Inherits Base
'some methods/properties here needed by Child classes
End Class
Public MustInherit Class Intermediate(Of T As Class)
Inherits Intermediate
'implement generic Content property here
End Class
Then RealChild would inherit from the generic Intermediate and EmptyChild would inherit from the non-generic Intermediate. My problem with that solution is that the Base class is in a separate assembly and I need to keep the code that handles the generic type in that assembly. And there is functionality in the Intermediate class that does not belong in the assembly with the Base class.
Yes, you need to specify a type parameter when you inherit, or your EmptyChild must be generic as well. But, you don't have to dummy up a EmptyClass - just use Object as your type parameter:
Public Class EmptyClass
Inherits Intermediate(Of Object)
End Class