M Kenyon II
M Kenyon II

Reputation: 4264

Pass an argument to a generic type New constructor in VB.Net Generics

I'm trying to be able to pass a Type parameter to a function called ConvertList, and have that function create some instances of the specified type. So, if I passed in type Foo, the function would create new objects of type Foo and put the created objects into a custom List object (SLMR_OBjList).

The function is in a generic class that is defined:

Public Class BOIS_Collection_Base(Of T)  

The function would accept types other than what is passed in the class definition. So, if we create an instance of BOIS_Collection_Base(Of MyTypeA) we may call the function ConvertList(Of MyTypeB).

I want the private variable _convertedList to be of a different type than the class. Is this possible? I can only seem to define it with (Of T).

Here is what I have so far:

Public Class BOIS_Collection_Base(Of T)  
    Private _convertedList As SLMR_ObjList(Of T) ' I can only seem to define this as (Of T), but want to make sure I can pass in a Type other than the Base(Of T)
    Public Function ConvertedObjList(Of myT)() As SLMR_ObjList(Of T) ' Should this be (Of T) or (Of myT) since I want it to use whatever Type is passed in
        For Each tempVar In Me.ObjList
            Dim newitem As myT = Activator.CreateInstance(GetType(myT), tempVar)
            ' Next line won't compile, says on newitem 'Value of type 'myT' cannot be converted to 'T'
            _convertedList.Add(newitem)
        Next
        _convertedList.Sort_Direction = Me.Sort_Direction
        _convertedList.Sort_Expression_List = Me.Sort_Expression_List
        Return _convertedList
    End Function

Here is what I would like to be able to do:

Dim mainCollInstance As New BOIS_Collection_Base(Of MyTypeA)
.... 
'Code that populates the BOIS_Collection_Base.ObjList property with an SLMR_ObjList(Of MyTypeA)
....
' Now I want to take that ObjList, and cast all the items in it to MyTypeB
Dim newListObj As SLMR_ObjList(Of MyTypeB) = mainCollInstance.ConvertList(Of MyTypeB)

Is this possible? Am I going about it wrong?

In response to Plutonix:
If I define _convertedList inside the method, like this:

Public Function ConvertedObjList(Of myT)() As SLMR_ObjList(Of myT)
    Dim _convertedList = New SLMR_ObjList(Of myT)

my errors go away, and the method does what I want, but _convertedList is no longer persistant in the object.

Upvotes: 1

Views: 2620

Answers (2)

Based on the previous related question and an assumption that MyTypeA and MyTypeB inherit from the same class (never got an answer), you may not need Generics for this. At any rate, this should help with the ctor part of the question. I do not as yet see where Generics fit in since inheritance may do what you want already:

Class MustInherit BiosItem
    Public Property Name As String
    Public Property TypeCode As String 
    ...
    MustOverride Function Foo(args...) As Type

    Overridable Property FooBar As String    
    ' etc - the more stuff in the base class the better

End Class
Class TypeA
    Inherits ABClass

    Public Sub New
       MyBase.New        ' stuff common to all child types
       TypeCode = "A"    ' EZ type ID rather than GetType
       ...
    End Sub
End Class

Class TypeB would be the same, but initialize TypeCode to "B". The same for C-Z. These allow you to poll the object rather than needing GetType: If thisObj.TypeCode = "A" Then.... Now, the collection class:

Public Class BIOSItems
    Inherits Collection(Of BiosItem)
    ' inheriting from Collection<T> provides Add, Count, IndexOf for us
    '    most important the Items collection
    '
End Class

Typing the collection as BiosItem will allow TypeA or TypeJ or TypeQ in it. As is, your collection will hold one Type only as it should be. This works because an item which is GetType(TypeA) is also GetType(BiosItem). See also note at the end.

Converting one item to another would seem to be something that would largely be handled by the NEW item being created or converted to. Since they are likely to be very similar then it can be handled by a constructor overload (if they are not similar, well we are well down the wrong road):

' a ctor overload to create the new thing based on the old things props
Public Sub New(oldThing As BiosItem)
    MyClass.New           ' start with basics like TypeCode, MyBase.New

    With BiosItem         ' coversion
        myFoo = .Foo
        myBar = .Bar       ' copy common prop vals to self
        ...
        Select Case .TypeCode
            Case "B"
                myProp1 = .Prop33     ' conversions
                myProp3 = .Prop16 + 3.14
                ... 
        End Select

        ' then initialize stuff unique to this type maybe
        ' based on other props
        If .PropX = "FooBar" Then myPropZ = "Ziggy"
     End With
End Sub

Code to create, convert, store:

Dim varOldBItem As TypeB = myBiosCol(ndx)   ' get old item
Dim varAItem As New TypeA(varOldBItem)      ' call the ctor above
myBiosCol.Add(varAItem)                     ' add new item
myBiosCol.Remove(varoldBItem)               ' delete the old if need be

If BOIS_Collection_Base is always supposed to contain MyTypeA, then type it that way (inheriting from Collection<T> still seems in order). If also MyTypeB objects are never added to the collection directly, but converted to MyTypeA first (Edit makes that less clear), then most of the above still applies, except for the inheritance. A ctor overload on MyTypeA could still take an old B object and create itself based on it. I'd be less inclined to do it via the ctor if they do not inherit from the same base class, but it could be done.

Upvotes: 0

Steven Doggart
Steven Doggart

Reputation: 43743

If you want to persist the list, then you can't really allow the consuming code to pass a different type for the list each time. That doesn't really make much sense, unless each time it's called, you only want the function to return the portion of the persisted list which contains objects of the given type. If that's the case, then you just need to declare _convertedList As SLMR_ObjList(Of Object) and then filter it and convert it to the correct type as necessary.

If, however, as I suspect is the case, the consumer will always be requesting that it be converted to the same type each time the function is called, then that output type is not really a property of the function call. Rather, it's a property of the whole class. In that case, you should make your class take two generic type arguments, like this:

Public Class BOIS_Collection_Base(Of T, TOut)
    Private _convertedList As SLMR_ObjList(Of TOut)

    Public Function ConvertedObjList() As SLMR_ObjList(Of TOut)
        For Each tempVar As T In Me.ObjList
            Dim newitem As TOut = DirectCast(Activator.CreateInstance(GetType(TOut), tempVar), TOut)
            ' Next line won't compile, says on newitem 'Value of type 'myT' cannot be converted to 'T'
            _convertedList.Add(newitem)
        Next
        _convertedList.Sort_Direction = Me.Sort_Direction
        _convertedList.Sort_Expression_List = Me.Sort_Expression_List
        Return _convertedList
    End Function
End Class

Upvotes: 1

Related Questions