是否有更快捷的方式将Fun<TEntity, TId>
投射到Func<TEntity, object>
public static class StaticAccessors<TEntity>
{
public static Func<TEntity, TId> TypedGetPropertyFn<TId>(PropertyInfo pi)
{
var mi = pi.GetGetMethod();
return (Func<TEntity, TId>)Delegate.CreateDelegate(typeof(Func<TEntity, TId>), mi);
}
public static Func<TEntity, object> ValueUnTypedGetPropertyTypeFn(PropertyInfo pi)
{
var mi = typeof(StaticAccessors<TEntity>).GetMethod("TypedGetPropertyFn");
var genericMi = mi.MakeGenericMethod(pi.PropertyType);
var typedGetPropertyFn = (Delegate)genericMi.Invoke(null, new[] { pi });
//slow: lambda includes a reflection call
return x => typedGetPropertyFn.Method.Invoke(x, new object[] { }); //can we replace this?
}
}
有没有办法将typedGetPropertyFn
转换为Func<TEntity, object>
而不在返回的lambda中使用反射代码,如上例所示?
编辑:添加修改后的解决方案
好的,感谢280Z28引导我走下正确的道路,我已将其纳入下面的最终解决方案中。我已将反射代码留在那里,用于不支持表达式的平台。对于获得int
和string
属性,平台显示 26x 到 27x (13 / .5 ticks avg)性能提升。< / p>
public static Func<TEntity, object> ValueUnTypedGetPropertyTypeFn(PropertyInfo pi)
{
var mi = typeof(StaticAccessors<TEntity>).GetMethod("TypedGetPropertyFn");
var genericMi = mi.MakeGenericMethod(pi.PropertyType);
var typedGetPropertyFn = (Delegate)genericMi.Invoke(null, new[] { pi });
#if NO_EXPRESSIONS
return x => typedGetPropertyFn.Method.Invoke(x, new object[] { });
#else
var typedMi = typedGetPropertyFn.Method;
var obj = Expression.Parameter(typeof(object), "oFunc");
var expr = Expression.Lambda<Func<TEntity, object>> (
Expression.Convert(
Expression.Call(
Expression.Convert(obj, typedMi.DeclaringType),
typedMi
),
typeof(object)
),
obj
);
return expr.Compile();
#endif
}
答案 0 :(得分:7)
如您所知,您可以从MethodInfo
获得PropertyInfo.GetGetMethod()
。从那里,您可以使用以下内容来获取Func<object, object>
来检索该属性。通过类似的方法,您可以返回强类型Func<TObject, TResult>
。对于任何给定的MethodInfo
,如果您需要多次,则应该缓存此调用的结果,因为此方法比调用生成的委托至少要贵一个数量级。
private static Func<object, object> BuildAccessor(MethodInfo method)
{
ParameterExpression obj = Expression.Parameter(typeof(object), "obj");
Expression<Func<object, object>> expr =
Expression.Lambda<Func<object, object>>(
Expression.Convert(
Expression.Call(
Expression.Convert(obj, method.DeclaringType),
method),
typeof(object)),
obj);
return expr.Compile();
}
答案 1 :(得分:5)
在.NET 4.0中,您可以执行此操作,因为Func委托使用out修饰符标记TResult。 .NET 3.5不支持generic covariance/contravariance,因此您不能简单地进行转换。我不确定是否还有另一种聪明的方法,它比反射更快。
这是the .NET 4.0 doc page for Func。请注意,TResult标记为“out”,因此其返回值可以转换为特定于较少的类型,例如object。
<小时/> 对于没有外部依赖关系的快速示例,以下代码无法在.NET 3.5上编译,但在.NET 4.0上编译并正确运行。
// copy and paste into LINQpad
void Main()
{
Func<int, string> func1 = GetString;
string res1 = func1(1);
res1.Dump();
Func<int, object> func2 = func1;
object res2 = func2(1);
res2.Dump();
}
public string GetString<T>(T obj) {
return obj.ToString();
}
答案 2 :(得分:5)
您是否考虑过执行以下操作:
Func<Foo, Bar> typed = (f) => return new Bar();
Func<Foo, object> untyped = (f) => typed(f);
这样你就可以包装委托了。