我该如何获得对象的所有公共属性的速度比用PropertyInfo.GetValue()?我该、如何获得、属性、对象

2023-09-06 11:11:01 作者:怪咖小青年

我有以下的通用code,它迭代的IEnumerable< T> (假设所有的对象都完全型 T ),并为每个项目它会检索所有属性的值,并将其追加到一个的StringBuilder

I have the following generic code that iterates over IEnumerable<T> (assuming all objects are exactly of type T) and for each item it retrieves all properties values and appends them to a StringBuilder:

Type t = typeof(T);
var properties = t.GetProperties();

foreach (var item in list)
{
    foreach (var property in properties)
    {
        object value = property.GetValue(item, null);
        // feed to StringBuilder
    }
}

和BTW的的IEnumerable 来自一个不平凡的LINQ到SQL查询。

and btw the IEnumerable comes from a non-trivial Linq-To-Sql query.

所以我的资料我的code和发现,我所有的code 37,3%的时间都用在 System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader.MoveNext() 和31,59%的时间都用在 RuntimePropertyInfo.GetValue()

So I profile my code and find that of all my code 37,3% time is spent in System.Data.Linq.SqlClient.ObjectReaderCompiler.ObjectReader.MoveNext() and 31,59% time is spent in RuntimePropertyInfo.GetValue().

这就是为什么我很高兴找到检索所有属性的另一种方式 - 一个比调用 PropertyInfo.GetValue()

This is why I'd be happy to find another way of retrieving all properties - one that is faster than calling PropertyInfo.GetValue().

我怎么能做到这一点(preferable保持code通用)?

How could I achieve that (preferable keeping the code generic)?

推荐答案

的策略是缓存未连接到一个特定对象的任何实例委托。这是一个小麻烦比它的声音。

The strategy is to cache a delegate that is not attached to any instance of a particular object. This is a little trickier than it sounds.

乔恩斯基特的在话题文章,有利于一般情况下,但是我想我会为此提供特定的情况下,简化的解决方案。

Jon Skeet's article on the topic is good for the general case, however I thought I'd offer a simplified solution for this specific case.

主力是 Delegate.CreateDelegate 这需要一个的MethodInfo ,并返回一个代表,是以实例作为参数。该的MethodInfo PropertyInfo.GetGetMethod 提供。诀窍是,我们希望所有的代表返回对象,但我们不能简单地用 Func键&LT; T,对象&gt; 作为委托类型。这种类型是不会在没有一个拳击指令值类型兼容。

The workhorse is Delegate.CreateDelegate which takes a MethodInfo and returns a delegate that takes the instance as a parameter. The MethodInfo is provided by PropertyInfo.GetGetMethod. The trick is that we want all the delegates to return an object but we can't simply use Func<T, object> as the delegate type. That type is not going to be compatible without a boxing instruction for value types.

要做到这一点,我们利用一个辅助方法(下它被称为 CreateDelegateInternal ),我们将通过反思与正确的运行时类型调用。因为我们只加载代表时称这一次,这是不是太昂贵。然后,我们将包裹强类型的代表在一个不太强类型的lambda将插入正确的转换。

To do that we leverage a helper method (below it is called CreateDelegateInternal) which we will invoke by reflection with the correct run-time types. Since we only call this once when loading the delegates, it is not too costly. Then we will wrap the strongly-typed delegate in a less strongly typed lambda which will insert the correct conversion.

public static class ReflectionHelper<T>
{
    private static readonly IEnumerable<Func<T, object>> propertyDelegates = CreateDelegates().ToArray();

    private static IEnumerable<Func<T, object>> CreateDelegates()
    {
        var helper = typeof(ReflectionHelper<T>).GetMethod("CreateDelegateInternal", BindingFlags.Static | BindingFlags.NonPublic);
        var properties = typeof(T).GetProperties();
        foreach(var property in properties)
        {
            var method = helper.MakeGenericMethod(typeof(T), property.PropertyType);
            yield return (Func<T, object>)method.Invoke(null, new object[] { property.GetGetMethod() });                
        }
    }

    private static Func<T, object> CreateDelegateInternal<T, TReturn>(MethodInfo m)
    {
        var f = (Func<T, TReturn>)Delegate.CreateDelegate(typeof(Func<T, TReturn>), m);
        return t => (object)f(t);
    }

    public static IEnumerable<Func<T, object>> Properties 
    {
        get { return propertyDelegates; }
    }
}

和您的code看起来是这样的:

and your code would look something like:

foreach (var item in list)
{
    foreach (var property in ReflectionHelper<T>.Properties)
    {
        object value = property(item);
        // feed to StringBuilder
    }
}

显然,你可能希望可以增加一些验证(检查属性的的CanRead 属性)和错误处理。此外,您可能希望元组了的PropertyInfo 的输出,所以你可以打印如姓名的元数据,这是留给简单清晰。

Obviously, you may want may add some validation (to check the property's CanRead property) and error handling. Also, you may want to tuple up the PropertyInfo in the output so you could print metadata like names, this is left simple for clarity.

 
精彩推荐