20

有没有更快的投射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?
 }
}

有没有办法像上面的例子一样在返回的 lambda 中没有反射代码的情况下转换typedGetPropertyFn为 a ?Func<TEntity, object>

编辑:添加修改后的解决方案

好的,感谢 280Z28 引导我走上正确的道路,我已将其包含在下面的最终解决方案中。对于不支持表达式的平台,我已经将反射代码留在了那里。对于这样做的平台,它显示获取和属性的性能提高了26 倍27 倍(13 / .5 刻度平均)。intstring

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
}
4

3 回答 3

10

您是否考虑过执行以下操作:

 Func<Foo, Bar> typed = (f) => return new Bar();
 Func<Foo, object> untyped = (f) => typed(f);

这样,您只需包装委托。

于 2010-01-30T19:31:52.590 回答
8

如您所知,您可以MethodInfoPropertyInfo.GetGetMethod(). 从那里,您可以使用以下内容来获取 aFunc<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();
}
于 2010-01-30T19:31:03.027 回答
6

在 .NET 4.0 中,您可以这样做,因为 Func 委托使用 out 修饰符标记 TResult。.NET 3.5 不支持泛型协变/逆变,因此您不能简单地进行转换。我不确定是否有另一种比反射更快的聪明方法。

这是Func 的 .NET 4.0 文档页面。请注意,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();
}
于 2010-01-30T19:18:58.573 回答