经过一段时间的调查,我找到了一个令我满意的解决方案。我想与社区分享。也许它对将来的某人有帮助。如果有人有更好的解决方案,请告诉我...
我意识到我的问题实际上与 XML 序列化有关(二进制序列化不会出现异常)。感谢 Aybe 将我推向这个方向。我想这个问题可以通过 DataContractSerializer 类来解决。不过,我实现了一些更接近本文主题的其他内容。
下面是初始示例的扩展,它更清楚地解决了问题(我希望如此)。它现在包括反序列化以显示实际问题是什么。抱歉,时间有点长
Public Interface IParent
Property ParentProp As String
End Interface
Public Interface IImportable
Inherits IParent
Property ChildProp As String
End Interface
<Serializable> Public Class ExampleClass
Implements IImportable
Public Property ChildProp As String = "Child Property" Implements IImportable.ChildProp
Public Property ParentProp As String = "Parent Property" Implements IParent.ParentProp
End Class
<Serializable> Public Class ExampleClass_NonImportableList
Inherits List(Of Integer)
Implements IParent
Public Property ParentProp As String = "Parent Property" Implements IParent.ParentProp
End Class
<Serializable> Public Class ExampleClass_List
Inherits List(Of Integer)
Implements IImportable
Public Property ChildProp As String = "List Child Property" Implements IImportable.ChildProp
Public Property ParentProp As String = "List Parent Property" Implements IParent.ParentProp
End Class
Public Class TestEnv
Private Enum En_DeSerializationMode
Xml
Binary
End Enum
Public Shared Sub Main()
'Define some instances
Dim locExampleClass = New ExampleClass
Dim locExampleClass_NonImportableList = New ExampleClass_NonImportableList From {1, 3, 5, 7}
Dim locExampleClass_List = New ExampleClass_List From {11, 13, 15, 17}
'Set mode and filenames
'Dim SeriMode = En_DeSerializationMode.Binary
Dim SeriMode = En_DeSerializationMode.Xml
Dim FileExt = If(SeriMode = En_DeSerializationMode.Binary, ".bin", ".xml")
Dim File1 = New IO.FileInfo("ExampleClass" & FileExt)
Dim File2 = New IO.FileInfo("ExampleClass_NonImportableList" & FileExt)
Dim File3 = New IO.FileInfo("ExampleClass_List" & FileExt)
'Export the instances
TestEnv.ExportToFile(File1, SeriMode, locExampleClass)
TestEnv.ExportToFile(File2, SeriMode, locExampleClass_NonImportableList)
TestEnv.ExportToFile(File3, SeriMode, locExampleClass_List)
'Import form the files and add to lists
'Obviously complier error: TestEnv.CheckImportFromFile(Of Integer, ExampleClass)(New IO.FileInfo(File1 & FileExt), SeriMode, locExampleClass)
TestEnv.ImportFromFile(File1, SeriMode, locExampleClass) 'Direct import can be done
TestEnv.CheckImportFromFile(Of Integer, ExampleClass_NonImportableList)(File2, SeriMode, locExampleClass_NonImportableList)
TestEnv.CheckImportFromFile(Of Integer, ExampleClass_List)(File3, SeriMode, locExampleClass_List)
End Sub
Private Shared Sub ExportToFile(Of C)(ExportFile As IO.FileInfo, Mode As En_DeSerializationMode, InstanceTarget As C)
'Set the serialization routine according to the mode
Dim LambdaSerializer As Action
Select Case Mode
Case En_DeSerializationMode.Binary
LambdaSerializer = Sub()
Dim locFs = New IO.FileStream(ExportFile.FullName, IO.FileMode.Create)
Using locFs
Dim locBinaryFormatter = New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(Nothing,
New System.Runtime.Serialization.StreamingContext(System.Runtime.Serialization.StreamingContextStates.File))
locBinaryFormatter.Serialize(locFs, InstanceTarget)
locFs.Flush()
locFs.Close()
End Using
End Sub
Case Else 'En_DeSerializationMode.Xml
LambdaSerializer = Sub()
Dim locXmlWriter = New System.Xml.Serialization.XmlSerializer(InstanceTarget.GetType)
Dim locXmlFile = New IO.StreamWriter(ExportFile.FullName)
Using locXmlFile
locXmlWriter.Serialize(locXmlFile, InstanceTarget)
locXmlFile.Flush()
locXmlFile.Close()
End Using
End Sub
End Select
'Serialize the instance to the file
Try
LambdaSerializer.Invoke()
Catch ex As Exception
MessageBox.Show(ex.Message, "Export error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
End Sub
Private Shared Sub CheckImportFromFile(Of T, C As {IList(Of T), IParent})(ImportFile As IO.FileInfo, Mode As En_DeSerializationMode, ByRef InstanceTarget As C)
'One dirty solution would be of course to implement IImportable to each class passed to CheckImportFromFile: But this would make no sense, craete effort and would just satisfy the compiler
If TypeOf (InstanceTarget) Is IImportable Then
Dim Imported As IImportable = Nothing
'Dim Imported As C 'This creates a compiler error because C does not implement IImportable
'Dim Imported As {C,IImportable} 'PseudoCode!!!
TestEnv.ImportFromFile(ImportFile, Mode, Imported) ' This causes an exception when using Xml deserialization (binary works)
If Imported IsNot Nothing Then
Dim ImportedList As C
Try
'Try block to prevent that an instance is imported that by incident fulfills the C restrictions but is not of InstanceTarget type
ImportedList = DirectCast(Imported, C)
Catch ex As Exception
MessageBox.Show("The imported instance of type " & Imported.GetType.ToString & " could not be converted to the target type " & GetType(C).ToString, "Import error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Exit Sub
End Try
'Add the imported items to the target instance list
For Each locItem In ImportedList
InstanceTarget.Add(locItem)
Next
End If
'Without restriction but with runtime checks
Dim Imported2 As C
TestEnv.ImportFromFile_RuntimeChecks(ImportFile, Mode, Imported2)
If Imported2 IsNot Nothing Then
'Now no trycast to C (respectively IList) is needed
For Each locItem In Imported2
InstanceTarget.Add(locItem)
Next
End If
'Final solution
Dim Imported3 As C
TestEnv.ImportFromFile_Final(ImportFile, Mode, Imported3, Function(locImported)
'This makes programmer aware that the imported instance has to be importable (try block in ImportFromFile method)
Return DirectCast(locImported, IImportable)
End Function)
If Imported3 IsNot Nothing Then
'Now no trycast to C (respectively IList) is needed
For Each locItem In Imported3
InstanceTarget.Add(locItem)
Next
End If
Else
'Something else is done (e.g. deactivating context menues)
MessageBox.Show("Instance of type " & GetType(C).ToString & " will not be imported", "No import", MessageBoxButtons.OK, MessageBoxIcon.Information)
End If
End Sub
Private Shared Sub ImportFromFile(Of C As {IImportable})(ImportFile As IO.FileInfo, SerializationMode As En_DeSerializationMode, ByRef ImportedInstance As C)
ImportedInstance = TestEnv.FileDeserializer(Of C)(ImportFile, SerializationMode)
If ImportedInstance Is Nothing Then Exit Sub
'Do something after import
ImportedInstance.ChildProp += " (I was imported)"
End Sub
Private Shared Sub ImportFromFile_RuntimeChecks(Of C As {IParent})(ImportFile As IO.FileInfo, SerializationMode As En_DeSerializationMode, ByRef ImportedInstance As C)
ImportedInstance = TestEnv.FileDeserializer(Of C)(ImportFile, SerializationMode)
If ImportedInstance Is Nothing Then Exit Sub
'This would work but is not very nice (there is a reason why C shall be restricted to IImportable in the first place - no ifs or trycasts needed)
If TypeOf (ImportedInstance) Is IImportable Then
'Do something after import
DirectCast(ImportedInstance, IImportable).ChildProp += " (I was imported with RuntimeChecks)"
Else
Throw New Exception("The input parameter needs to be of type IImportable but I (or the compiler :-)) am too stupid to make it work")
End If
End Sub
Private Shared Sub ImportFromFile_Final(Of C As {IParent})(ImportFile As IO.FileInfo, SerializationMode As En_DeSerializationMode, ByRef ImportedInstance As C, ImportedCaster As Func(Of C, IImportable))
ImportedInstance = TestEnv.FileDeserializer(Of C)(ImportFile, SerializationMode)
If ImportedInstance Is Nothing Then Exit Sub
Dim ImportedInstanceCast As IImportable
Try
ImportedInstanceCast = ImportedCaster.Invoke(ImportedInstance)
Catch ex As Exception
MessageBox.Show("Instance of type " & ImportedInstance.GetType.ToString & " cannot be converted to IImportable", "No import", MessageBoxButtons.OK, MessageBoxIcon.Information)
Exit Sub
End Try
'Do something after import
ImportedInstanceCast.ChildProp += " (I was imported smart)"
End Sub
Private Shared Function FileDeserializer(Of C)(ImportFile As IO.FileInfo, SerializationMode As En_DeSerializationMode) As C
'Check that the file exists
If Not ImportFile.Exists Then
MessageBox.Show("File does not exist", "File error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return Nothing
End If
'Define reader
Dim LambdaDeserializer As Func(Of Object)
Select Case SerializationMode
Case En_DeSerializationMode.Binary
LambdaDeserializer = Function()
Dim locFs = New IO.FileStream(ImportFile.FullName, IO.FileMode.Open)
Dim locObject As Object
Using locFs
Dim locBinaryFormatter = New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(Nothing,
New System.Runtime.Serialization.StreamingContext(System.Runtime.Serialization.StreamingContextStates.File))
locObject = locBinaryFormatter.Deserialize(locFs)
locFs.Close()
End Using
Return locObject
End Function
Case Else 'En_DeSerializationMode.Xml
LambdaDeserializer = Function()
Dim locXmlReader = New System.Xml.Serialization.XmlSerializer(GetType(C))
Dim locXmlFile = New IO.StreamReader(ImportFile.FullName)
Dim locObject As Object
Using locXmlFile
locObject = locXmlReader.Deserialize(locXmlFile)
locXmlFile.Close()
End Using
Return locObject
End Function
End Select
'Deserialize the file
Dim ImportedInstance As C
Try
Dim DeseriObj = LambdaDeserializer.Invoke
ImportedInstance = DirectCast(DeseriObj, C)
Catch ex As Exception
MessageBox.Show("Error during deserialization: " & ex.Message, "Import error", MessageBoxButtons.OK, MessageBoxIcon.Error)
End Try
Return ImportedInstance
End Function
End Class
稍微描述一下例子:
我认为这在代码通信方面已经足够了。如前所述,如果有人有更好的主意,请发布...