通用功能和动作/返回取决于类型

时间:2019-05-06 21:38:17

标签: vb.net generics

我有一个反序列化由API发送的自定义序列化函数。 我想构建一个泛型函数,以便反序列化的对象不是Object类型,而是正确的类型。

包含序列化对象的字符串可以反序列化为以下类型之一:

  • 一个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

我可以决定将每个Type拆分为一个单独的函数。我想避免这样的情况,以至于我最终没有得到6个功能。这是因为在对字符串进行反序列化之前,我还需要对字符串运行一些其他操作。对于故事,字符串具有各种编码/加密格式。因此,如果我有4种格式,那么现在我需要处理4x6 = 24个函数。

我很乐意将所有解码/反序列化封装到一个函数中:Dim MyObject as Something = Deserialize(Of Something)(StringFromAPI, MyEncodingEnumOptions.Option42)

非常感谢!

2 个答案:

答案 0 :(得分:0)

根据特定变量的类型执行特定操作:感觉类似于“重载”,不同之处在于,此操作应基于输出变量的类型,而不是根据输入变量的类型执行操作

不幸的是,不可能重载泛型函数的TypeName。例如,Function MyFunction(Of T as New)(SomeParameter as String) as TFunction 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.

答案 1 :(得分:0)

一种替代方法是使用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