循环中的有效反射

时间:2016-11-16 08:14:41

标签: c# reflection

在循环中使用反射时遇到性能问题。问题是我用它来重复访问长依赖链末端的对象。例如,在类似的情况下

class FirstObject 
{
    public SecondObject sO;
}

class SecondObject
{
    public ThirdObject tO;
}

class ThirdObject
{
    public FourthObject fO;
}

class FourthObject
{
    public object neededValue;
}

由于我只对最后一个对象所包含的值感兴趣,因此我需要使用GetProperty().GetValue()

重复遍历整个链。

FirstObject - > SecondObject - > ThirdObject - > FourthObject [neededValue]

有没有办法,也许是一些API,可以用来缩短链条,或者只是在这种情况下保存neededValue的整个路径?

澄清

我需要使用包含FirstObjects的列表来执行此操作。我无法重写代码来降低嵌套级别:它是自动生成的。

2 个答案:

答案 0 :(得分:2)

您可以使用一种技巧而不是反射GetValue()。它肯定更快,但代码可读性会更差。

object GetPropertyValue(object obj, string propertyName)
{
    MethodInfo propertyGetter = obj.GetType().GetMethod("get_" + propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
    Func<object> getPropertyValue = (Func<object>)Delegate.CreateDelegate(typeof(Func<object>), obj, propertyGetter);
    return getPropertyValue();
}

在这里,我们使用了一点“黑客”知道属性getter只是一个具有预定义名称的方法:get_<PropertyName>()

如果您的对象层次结构每次都相同,您甚至可以从上面的示例中缓存propertyGetter对象并重复使用它。

更新

您甚至可以在没有具体对象引用的情况下为属性getter创建委托。因此,您可以将此委托与许多对象(相同类型)一起使用:

Func<ObjectType, object> getPropertyValue = (Func<ObjectType, object>)Delegate.CreateDelegate(typeof(Func<ObjectType, object>), propertyGetter);

ObjectType obj;
var propertyValue = getPropertyValue(obj);

如果您缓存getPropertyValue()委托,那么效果将比调用GetValue()方法更好。

答案 1 :(得分:1)

假设您知道根对象的类型以及您感兴趣的成员的路径,您可以在循环之外准备一个Func<object, object>委托并在其中使用它。这样您就可以消除GetProperty / GetFieldGetValue反映费用。

准备此类委托的最简单方法是使用System.Linq.Expressions.Expression方法构建和编译lambda表达式:

public static class SelectorFactory
{    
    public static Func<object, object> GetSelector(Type type, string memberPath)
    {
        return CreateSelector(type, memberPath);
    }

    static Func<object, object> CreateSelector(Type type, string memberPath)
    {
        var parameter = Expression.Parameter(typeof(object), "source");
        var source = Expression.Convert(parameter, type);
        var value = memberPath.Split('.').Aggregate(
            (Expression)source, Expression.PropertyOrField);
        if (value.Type.IsValueType)
            value = Expression.Convert(value, typeof(object));
        // (object source) => (object)((T)source).Prop1.Prop2...PropN
        var selector = Expression.Lambda<Func<object, object>>(value, parameter);
        return selector.Compile();
    }
}

使用您的示例进行测试:

// This would be outside of the loop
var selector = SelectorFactory.GetSelector(typeof(FirstObject), "sO.tO.fO.neededValue");
// and this inside (of course instead of new you would get item from a list)
var item = new FirstObject { sO = new SecondObject { tO = new ThirdObject { fO = new FourthObject { neededValue = "Ivan" } } } };
var value = selector(item);

P.S。如果你想知道为什么我使用了两个辅助方法(一个公共方法和一个私有方法),那是因为在某些时候你可以轻松添加选择器缓存,例如添加一个字典并只更改公共方法实现,如下所示:

static readonly Dictionary<Tuple<Type, string>, Func<object, object>> selectorCache = new Dictionary<Tuple<Type, string>, Func<object, object>>();

public static Func<object, object> GetSelector(Type type, string memberPath)
{
    var key = Tuple.Create(type, memberPath);
    Func<object, object> value;
    lock (selectorCache)
    {
        if (!selectorCache.TryGetValue(key, out value))
            selectorCache.Add(key, value = CreateSelector(type, memberPath));
    }
    return value;
}