在循环中使用反射时遇到性能问题。问题是我用它来重复访问长依赖链末端的对象。例如,在类似的情况下
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的列表来执行此操作。我无法重写代码来降低嵌套级别:它是自动生成的。
答案 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
/ GetField
和GetValue
反映费用。
准备此类委托的最简单方法是使用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;
}