[Delegate] .CreateDelegate设置属性性能

时间:2012-09-04 19:54:32

标签: .net reflection delegates propertyinfo

这是我一直在研究的序列化程序,它根据一些映射的属性属性将数据行序列化为对象。我阅读了一些与使用[Delegate] .CreateDelegate来提高速度相关的帖子 - 所以我试了一下。它似乎已经获得了一些性能提升,但最小化(即使在所有对象类型的初始循环以缓存所有可能的属性信息对象等)之后。映射的属性信息只有一个可以分配的备用数据库列名,因为我们无法完全控制我使用的所有列名。它也只抓取我要映射的属性。

目前,序列化约。每个对象平均有13个属性的1500个对象,以及1500个父对象(作为子对象)每个平均有3个对象也被平均填充了13个属性 - 这些时间用于我的比较:

手动设置property.Name = row.Item(“columnName”)
785毫秒

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

1 个答案:

答案 0 :(得分:2)

我建议您直接查看FastMember,或者只是借用并调整代码。特别是(切换到C#):

var accessor = TypeAccessor.Create(targetType); 
foreach(PropertyInfo p in MappedPropertiesCache.Item(targetType))
{
    ...
    accessor[target, p.Name] = ... // newValue
    ...
}

或者,看看像dapper-dot-net这样的东西如何处理成员分配。

作为旁注:静态字典不是线程安全的,您可能需要小心访问它。