我有一个反序列化由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)
但是,我很快受到限制,主要是因为:
String
类型,因为它无法强制转换
进入T。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)
非常感谢!
答案 0 :(得分:0)
根据特定变量的类型执行特定操作:感觉类似于“重载”,不同之处在于,此操作应基于输出变量的类型,而不是根据输入变量的类型执行操作
不幸的是,不可能重载泛型函数的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.
答案 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