Общая функция и действие / возврат в зависимости от типа - PullRequest
0 голосов
/ 07 мая 2019

У меня есть функция, которая десериализует некоторые пользовательские сериализации, отправленные API.Я хочу построить универсальную функцию, чтобы десериализованный объект имел тип не Object, а правильный тип.

Строки, содержащие сериализованный объект, можно десериализовать в один из следующих типов:

  • A String,
  • IList(Of String),
  • IDictionnary(Of String),
  • один из множества SomeNameContainer классов, все производныеиз класса BaseContainer,
  • IList(Of SomeNameContainer) или
  • IDictionnary(Of SomeNameContainer).

Я хотел бы иметь один Function Deserialize(Of T)(MyString as String) as T.Внутри этой функции я попытался выполнить несколько тестов Select Case T: GetType(String):Etc, чтобы отделить различные действия для запуска на MyString, в зависимости от ожидаемого объекта, который нужно создать от десериализации.

Например, десериализация вSomeNameContainer обычно выполняется с помощью другой универсальной функции: Dim Deserialized as SomeNameContainer = GetFromContainer(SomeNameContainer)(MyString)

Однако я быстро ограничен, главным образом потому, что:

  1. Я не могу вернуть тип String, потому что онне может привести его к T.
  2. String - тип значения, в то время как SomeNameContainer - классы.Поэтому невозможно добавить ограничение (Of T As {New}).Это означает, что я не могу сделать что-то вроде Dim NameContainer as New T: If TypeOf NameContainer Is BaseContainer, чтобы применить одну и ту же операцию ко всем классам, производным от BaseContainer.

Один трек, который я нашел, - это использование CTypeDynamic(Of T)(obj as object),который бросает во время выполнения.Это могло бы решить проблему 1, но проблема 2 все еще включена.

Function Deserialize(Of T)(MyString as String) as T

    Select Case GetType(T)

        Case GetType(String)
            Return SomeFunction(String) '<- Only run-time casting allowed: Return CTypeDynamic(Of String)(SomeFunction(String))

        Case GetType(IList(Of String)
            Return SomeOtherFunction(String)

        Case GetType(...)
            '...

        Case Else

            Dim MyContainer as New T '<- Not Allowed to use New

            if TypeOf MyContainer Is T then
                Return GetFromContainer(Of T)(String)
            else
                '...
            End If

    End Select

End Function

Я мог бы решить разделить каждый Тип в отдельную функцию.Я хотел бы избежать, чтобы у меня не было 6 функций.Это потому, что мне также нужно выполнить некоторые другие операции над строкой перед ее десериализацией.Для истории строки подпадают под различные форматы кодирования / шифрования.Поэтому, если у меня есть 4 формата, то теперь мне нужно иметь дело с 4x6 = 24 функциями.

Я бы хотел иметь такую ​​роскошь, как инкапсуляцию всего декодирования / десериализации в одну функцию: Dim MyObject as Something = Deserialize(Of Something)(StringFromAPI, MyEncodingEnumOptions.Option42)

Большое спасибо заранее!

Ответы [ 2 ]

0 голосов
/ 08 мая 2019

Альтернативой является использование поздней привязки / инициализации объекта во время выполнения, используя Activator.CreateInstance(Of T).Типичное переключение T будет выглядеть следующим образом:

Public Function GetDeserializedObject(Of T)(ByVal MyString As String) As T

    Select Case GetType(T)

        Case GetType(String)

            Return CTypeDynamic(MyString, GetType(T)) '<-- Runtime Casting

        Case Else

            If Not MyString.IsDeserializable Then Throw New ArgumentException(String.Format("Unable to deserialize to a {0} object: The provided string is not valid.", GetType(T).ToString))

            Select Case GetType(T)

                Case GetType(IList(Of String))
                    Return CollectionString.ToStringList(MyString)

                Case Else

                    Dim MyReturn As T = Activator.CreateInstance(Of T) '<-- Object instantiation to the type provided at Runtim 

                    If TypeOf MyReturn Is BaseContainer Then '<-- Now we can use TypeOf ... Is ... which will return True for all Object derived from BaseContainer 
                        Return Activator.CreateInstance(GetType(T), MyString)

                    ElseIf TypeOf MyReturn Is IList(Of BaseContainer) Then
                        Dim MyCollectionString As CollectionString = MyString
                        Return MyCollectionString.ExportToContainerList(MyReturn.GetType)

                    Else

                        Throw New ArgumentException(String.Format("Unable to deserialize to a {0} object: This type of object is not supported.", GetType(T).ToString))

                    End If

            End Select

    End Select

End Function
0 голосов
/ 07 мая 2019

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

К сожалению, невозможно перегрузить TypeName обобщенной функции. Например, Function MyFunction(Of T as New)(SomeParameter as String) as T и Function MyFunction(Of T as Structure)(SomeParameter as String) as T не могут сосуществовать в одном и том же пространстве имен.

Альтернативой является передача ожидаемого типа вывода в качестве входного аргумента, чтобы можно было выполнить обычную перегрузку: Sub MyFunction(ByVal SomeParameter as String, ByRef OutputVar as SomeType). Каждая перегрузка включает в себя различные SomeType TypeName.

Вывод «функции» сохраняется в OutputVar, который передается ByRef и извлекается после выполнения Sub:

Dim MyObject as Something = Deserialize(Of Something)(StringFromAPI, MyEncodingEnumOptions.Option42)

Становится

Sub Deserialize(ByRef MyObject as String, ByVal MyString As String, ByVal EncodingOption As MyEncodingEnumOptions)
    MyString = SomeDecoding(MyString,  EncodingOption)
    MyObject = SomeFunction(MyString)
End Sub

Sub Deserialize(ByRef MyObject as IList(Of String), ByVal MyString As String, ByVal EncodingOption As MyEncodingEnumOptions)
    MyString = SomeDecoding(MyString, EncodingOption)    
    MyObject = SomeOtherFunction(MyString)
End Sub

'...

Dim MyObject as Something
Deserialize(MyObject, StringFromAPI, MyEncodingEnumOptions.Option42)
'Now MyObject has been filled with the relevant data.
...