我有一个约30个项目的静态数据库,然后我创建了一个动态数据库(基本上是一个哈希表),由数千个对象组成,每个对象都引用静态数据库中的一个项目。然后我将动态db序列化为二进制并将其保存在文件中,然后加载它并反序列化。
我想要发生的是,加载时,为了保存内存,动态对象会引用相同的静态对象,但实际发生的是序列化,静态数据库对象按照每个动态对象进行序列化和存储并且在反序列化时,它们会创建大量应该是静态的基础对象的副本。
为了说明,我写了这个:
Public Class Form1
<Serializable> Public Class Banana
End Class
<Serializable> Public Class Orange
Public MyBanana As Banana
End Class
Public GreatBanana = New Banana
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim BF As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
Dim MS As New IO.MemoryStream
Dim FreshOrange = New Orange With {.MyBanana = GreatBanana}
BF.Serialize(MS, FreshOrange)
MS.Position = 0
Dim RefridgeratedOrange As Orange = BF.Deserialize(MS)
MsgBox(RefridgeratedOrange.MyBanana Is GreatBanana)
End Sub
End Class
“GreatBanana”变量扮演静态数据库的角色,其中包含一个项目。我们发现RefridgeratedOrange中的香蕉不再与FreshOrange中的香蕉相同(即使数据明智,它们也是相同的)
有没有办法向序列化程序指出此数据成员应该是静态成员?
答案 0 :(得分:0)
是的,您可以通过最终用户无法担心的方式来执行此操作,但是,它无法获得最快的算法(部分原因是每次请求MyBanana
属性,它会在缓存中检查)
我用您的代码编写了一个小测试用例来演示可能的解决方案。对于测试本身,我还添加了一个包含唯一ID的类,并覆盖了Equals
&amp; GetHashCode
方法,在Orange类中没有必要,但对于要添加到缓存中的所有项目都是必需的。
Orange类中的Property MyBanana
执行缓存本身的读取和设置,控制台程序执行一些基本测试以查看我们的假设是否正确。但正如我所说,不要用它来提高你的节目表现;)
Module Module1
Public NotInheritable Class CacheHolder
Private Shared ReadOnly cacheSet As HashSet(Of IDHolder) = New HashSet(Of IDHolder)
Public Shared Function GetFromCache(Of T As IDHolder)(obj As T) As T
If obj Is Nothing Then
Return obj
End If
Dim result As T
result = (From item In cacheSet Where item.Equals(obj) Select item).FirstOrDefault()
If result Is Nothing Then
cacheSet.Add(obj)
Return obj
End If
Return result
End Function
Public Shared Sub Add(Of T As IDHolder)(obj As T)
GetFromCache(obj)
End Sub
Public Shared ReadOnly Property Count As Integer
Get
Return cacheSet.Count
End Get
End Property
Public Shared ReadOnly Property Item(index As Integer) As IDHolder
Get
Return cacheSet(index)
End Get
End Property
Public Shared Function Contains(Of T As IDHolder)(item As T) As Boolean
Return cacheSet.Contains(item)
End Function
End Class
<Serializable>
Public Class IDHolder
Protected _id As Guid = Guid.NewGuid()
Public Property ID
Get
Return _id
End Get
Set(value)
_id = value
End Set
End Property
Public Overrides Function Equals(obj As Object) As Boolean
If Type.Equals(obj.GetType(), Me.GetType()) Then
Return Guid.Equals(DirectCast(obj, IDHolder).ID, Me.ID)
End If
Return MyBase.Equals(obj)
End Function
Public Overrides Function GetHashCode() As Integer
Return Me.ID.GetHashCode()
End Function
End Class
<Serializable> Public Class Banana
Inherits IDHolder
Public Property Title As String
End Class
<Serializable> Public Class Orange
Inherits IDHolder
Private _myBanana As Banana
Public Property MyBanana As Banana
Get
Return CacheHolder.GetFromCache(_myBanana)
End Get
Set(value As Banana)
_myBanana = CacheHolder.GetFromCache(value)
End Set
End Property
Public Property Title As String
End Class
Private Sub Serialize(Of T)(item As T, stream As IO.Stream)
Dim binaryFormatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
binaryFormatter.Serialize(stream, item)
End Sub
Private Function Deserialize(Of T)(stream As IO.Stream) As T
Dim binaryFormatter As New System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
If stream.CanSeek Then
stream.Seek(0, IO.SeekOrigin.Begin)
End If
Return binaryFormatter.Deserialize(stream)
End Function
Sub Main()
Dim MAX_CACHE As Integer = 100
Dim MAX_OBJECT As Integer = 10000
Console.WriteLine("Creating {0} bananas", MAX_CACHE)
For i As Integer = 1 To MAX_CACHE
Dim staticBanana As New Banana With {.Title = "Banana #" & i.ToString().PadLeft(3, "0")}
CacheHolder.Add(staticBanana)
Next
Dim rand As New Random()
Dim lstOfOranges As New List(Of Orange)
Console.WriteLine("Creating {0} Oranges", MAX_OBJECT)
For i As Integer = 1 To MAX_OBJECT
Dim index As Integer = rand.Next(CacheHolder.Count)
Dim orange As New Orange With {.MyBanana = CacheHolder.Item(index), .Title = "Orange #" & i.ToString().PadLeft(3, "0") & " with " & .MyBanana.Title}
lstOfOranges.Add(orange)
Next
Dim stream As New IO.MemoryStream
Console.WriteLine("Serializing")
Serialize(lstOfOranges, stream)
Console.WriteLine("Deserializing")
lstOfOranges = Deserialize(Of List(Of Orange))(stream)
Debug.Assert(CacheHolder.Count = MAX_CACHE, "The cacheholder holds more object than originally put in, nl: " & CacheHolder.Count)
Debug.Assert(lstOfOranges.Count = MAX_OBJECT, "The list of oranges isn't as big as it supposed to be, nl: " & lstOfOranges.Count)
Console.WriteLine("Getting distinct banana's")
Dim query = (From item In lstOfOranges Select item.MyBanana).Distinct().OrderBy(Function(i) i.Title).ToList()
' <= because it could be not all banana's were referenced with the randomizer
Debug.Assert(query.Count <= CacheHolder.Count, "There were more banana's in the distinct list than in the CacheHolder, this cannot be!")
Console.WriteLine("Checking if the distinct banana's were all found in the query")
For Each item In query
Debug.Assert(CacheHolder.Contains(item), item.ID.ToString() & " not found in CacheHolder!")
Dim cacheItem As IDHolder = CacheHolder.GetFromCache(item)
Debug.Assert(cacheItem Is item, "Item and cacheItem are not the same!")
Dim totalOranges As Integer = (From orange In lstOfOranges Where orange.MyBanana.Equals(cacheItem)).Count()
Console.WriteLine("Found {0} oranges attached to {1}", totalOranges, CType(cacheItem, Banana).Title)
Next
Console.WriteLine("Done")
Console.ReadLine()
End Sub
End Module