Передать аргумент в конструктор универсального типа New в VB.Net Дженерики

#vb.net #generics

#vb.net #generics

Вопрос:

Я пытаюсь передать параметр типа функции с именем ConvertList и заставить эту функцию создать несколько экземпляров указанного типа. Итак, если бы я передал тип Foo , функция создала бы новые объекты типа Foo и поместила созданные объекты в пользовательский объект списка (SLMR_OBjList ).

Функция находится в общем классе, который определен:

 Public Class BOIS_Collection_Base(Of T)  
  

Функция будет принимать типы, отличные от тех, которые передаются в определении класса. Итак, если мы создадим экземпляр BOIS_Collection_Base(Of MyTypeA) , мы можем вызвать функцию ConvertList(Of MyTypeB) .

Я хочу, чтобы частная переменная _convertedList была другого типа, чем класс. Возможно ли это? Кажется, я могу определить его только с помощью (Of T) .

Вот что у меня есть до сих пор:

 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
  

Вот что я хотел бы сделать:

 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)
  

Возможно ли это? Я ошибаюсь?

В ответ на Plutonix:
если я определю _convertedList внутри метода, например:

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

мои ошибки исчезают, и метод делает то, что я хочу, но _convertedList больше не сохраняется в объекте.

Комментарии:

1. Я не уверен, что это проблема generics, но как вы реализуете наследование. ЕСЛИ MyTypeA и MyTypeB связаны, то класс коллекции, вероятно, должен быть (Of AB_BaseClass) . Что еще более важно, если они связаны, то одному должно быть легко создать экземпляр самого себя из другого: newItem = New myTypeA(myTypeB)

2. Я не уверен, что понимаю это. Пожалуйста, ознакомьтесь с обновлениями.

3. Если вы хотите сохранить его, это означает, что вы всегда будете передавать один и тот же тип в ConvertedObjList(Of T) функцию, верно?

4. Хм… Я понимаю… возможно, нет. Возможно, я не захочу сохранять его на этом уровне. Спасибо, хорошая мысль. Возможно, я пытался заставить это делать слишком много.

Ответ №1:

Если вы хотите сохранить список, то вы не можете позволить потребляющему коду каждый раз передавать другой тип для списка. На самом деле это не имеет особого смысла, если только при каждом вызове вы не хотите, чтобы функция возвращала только ту часть сохраняемого списка, которая содержит объекты данного типа. Если это так, то вам просто нужно объявить _convertedList As SLMR_ObjList(Of Object) , а затем отфильтровать его и преобразовать в правильный тип по мере необходимости.

Однако, если, как я подозреваю, это так, потребитель всегда будет запрашивать преобразование его в один и тот же тип при каждом вызове функции, то этот тип вывода на самом деле не является свойством вызова функции. Скорее, это свойство всего класса. В этом случае вы должны заставить свой класс принимать два аргумента универсального типа, например:

 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
  

Ответ №2:

Основываясь на предыдущем соответствующем вопросе и предположении, что MyTypeA и MyTypeB наследуются от одного и того же класса (так и не получили ответа), вам могут не понадобиться Generics для этого. Во всяком случае, это должно помочь с ctor-частью вопроса. Я пока не вижу, где подходят дженерики, поскольку наследование может делать то, что вы уже хотите:

 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
  

Класс TypeB будет таким же, но инициализирует TypeCode на «B». То же самое для C-Z. Они позволяют вам опрашивать объект, а не использовать GetType: If thisObj.TypeCode = "A" Then... . Теперь класс коллекции:

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

Ввод коллекции as BiosItem позволит TypeA или TypeJ или TypeQ в ней. Как есть, ваша коллекция будет содержать только один тип, как и должно быть. Это работает, потому что элемент GetType(TypeA) , который является также GetType(BiosItem) . См. Также Примечание в конце.

Преобразование одного элемента в другой, по-видимому, будет в значительной степени обрабатываться создаваемым или преобразуемым новым элементом. Поскольку они, вероятно, будут очень похожи, это может быть обработано перегрузкой конструктора (если они не похожи, значит, мы идем по неправильному пути):

 ' 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
  

Код для создания, преобразования, хранения:

 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
  

Если BOIS_Collection_Base всегда предполагается, что он содержит MyTypeA , то введите его таким образом (наследование от Collection<T> все еще кажется в порядке). Если также MyTypeB объекты никогда не добавляются в коллекцию напрямую, но преобразуются в MyTypeA first (редактирование делает это менее понятным), то большинство из приведенных выше по-прежнему применяется, за исключением наследования. Перегрузка ctor в MyTypeA все равно может взять старый объект B и создать себя на его основе. Я был бы менее склонен делать это через ctor, если они не наследуются от одного и того же базового класса, но это можно было бы сделать.