#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, если они не наследуются от одного и того же базового класса, но это можно было бы сделать.