这是我一直在研究的一个序列化器,它基于一些映射的属性属性将数据行序列化为对象。我阅读了许多与使用 [Delegate].CreateDelegate 来提高速度相关的帖子 - 所以我试了一下。它似乎已经获得了一些性能提升,但很少(即使在所有对象类型的初始循环以缓存所有可能的属性信息对象等之后)。映射的属性信息只有一个可以分配的备用数据库列名,因为我们无法完全控制我使用的所有列名。它也只抓取我想要映射的属性。
现在,大约的序列化。1500 个对象,每个对象平均有 13 个属性,1500 个父对象(作为子对象)中的每个对象平均有 3 个对象,每个对象平均有 13 个属性 - 为了我的比较而夸耀这些时间:
手动设置 property.Name = row.Item("columnName")
785 ms
p.SetValue
1490 毫秒
GetSetMethod.Invoke
1585 毫秒
[委托].CreateDelegate
1285 毫秒
谁能建议我可以做更多的事情来提高性能?
Friend Class DataSerializer(Of T As Class)
Private Shared MappedPropertiesCache As New Dictionary(Of Type, PropertyInfo())
Private Shared MappedColumnNameCache As New Dictionary(Of Type, List(Of String))
Private Shared ActionCache As New Dictionary(Of String, Action(Of T, Object))
Friend Shared Function SerializeDataRow(ByVal row As DataRow) As T
Dim target As Object = Activator.CreateInstance(GetType(T))
Dim targetType As Type = GetType(T)
AnalyzeMappedCache(target, targetType)
Dim index As Integer = 0
Dim mappedColumns As List(Of String) = MappedColumnNameCache.Item(targetType)
'iterate through target object properties
For Each p As PropertyInfo In MappedPropertiesCache.Item(targetType)
If row.Table.Columns.Contains(mappedColumns.Item(index)) Then
'' SLOW
'p.SetValue(target, CheckDbNull(row.Item(mappedColumns.Item(index))), Nothing)
'' SLOWER
'Dim methodInfo As MethodInfo = p.GetSetMethod()
'methodInfo.Invoke(target, New Object() {CheckDbNull(row.Item(mappedColumns.Item(index)))})
''FASTER THAN PREVIOUS TWO, BUT STILL SLOW
'Dim key As String = String.Format("{0}:{1}", target.GetType.FullName, p.Name)
'If Not ActionCache.ContainsKey(key) Then
' Dim methodAction As Action(Of T, Object) = MagicMethod(p.GetSetMethod())
' ActionCache.Add(key, methodAction)
'End If
'Dim param As Object = CheckDbNull(row.Item(mappedColumns.Item(index)))
'If Not param Is Nothing Then
' ActionCache(key)(target, param)
'End If
End If
index = index + 1
Next
Return target
End Function
Private Shared Function MagicMethod(method As MethodInfo) As Action(Of T, Object)
' First fetch the generic form
Dim genericHelper As MethodInfo = GetType(DataSerializer(Of T)).GetMethod("MagicMethodHelper", BindingFlags.[Static] Or BindingFlags.NonPublic)
' Now supply the type arguments
Dim constructedHelper As MethodInfo = genericHelper.MakeGenericMethod(GetType(T), method.GetParameters()(0).ParameterType)
' Now call it. The null argument is because it's a static method.
Dim ret As Object = constructedHelper.Invoke(Nothing, New Object() {method})
' Cast the result to the right kind of delegate and return it
Return DirectCast(ret, Action(Of T, Object))
End Function
Private Shared Function MagicMethodHelper(Of TTarget As Class, TParam)(method As MethodInfo) As Action(Of TTarget, Object)
' Convert the slow MethodInfo into a fast, strongly typed, open delegate
Dim func As Action(Of TTarget, TParam) = DirectCast([Delegate].CreateDelegate(GetType(Action(Of TTarget, TParam)), method), Action(Of TTarget, TParam))
' Now create a more weakly typed delegate which will call the strongly typed one
Dim ret As Action(Of TTarget, Object) = Sub(target As TTarget, param As Object) func(target, CType(param, TParam))
Return ret
End Function
Private Shared Sub AnalyzeMappedCache(ByVal target As Object, ByVal targetType As Type)
'this assumes the target object inherits from BaseProperties
If Not MappedPropertiesCache.ContainsKey(targetType) Then
Dim props As PropertyInfo() = target.GetMappedProperties()
Dim mappedColumnNameList As New List(Of String)
For Each prop As PropertyInfo In props
mappedColumnNameList.Add(CType(prop.GetCustomAttributes(GetType(DTO_POMGMT.MappedProperty), True).FirstOrDefault, DTO_POMGMT.MappedProperty).ColumnName)
Next
MappedColumnNameCache.Add(targetType, mappedColumnNameList)
MappedPropertiesCache.Add(targetType, props)
End If
End Sub
'check for a dbnull value of any object type returned from database
Private Shared Function CheckDbNull(ByVal obj As Object) As Object
Return If(obj Is DBNull.Value, Nothing, obj)
End Function
End Class