将Func<T, T2>转换为Func<T, object>的更快方法是什么?

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?
 }
}
有没有一种方法可以将typedGetPropertyFn转换为Func<TEntity, object>,而不需要在返回的lambda中添加反射代码(就像上面的示例一样)?编辑:添加修改后的解决方案。好的,感谢280Z28给我指路,我将其包含在下面的最终解决方案中。我已经将反射代码保留在那里,以供不支持表达式的平台使用。对于支持表达式的平台,获取intstring属性的性能提高了26x27x(13 / .5 ticks avg)。
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
}
3个回答

10

您是否考虑过以下操作:

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

这样你只需包装委托即可。


我无法做这个,因为我没有Bar的编译时类型。 - mythz
2
如果您在编译时有“Bar”类型,这将是最简单和“奥林匹克”方式来完成XD。 - Guillermo Gutiérrez

9
如您所知,您可以从PropertyInfo.GetGetMethod()获取一个MethodInfo。通过此方法,您可以使用以下内容获取Func<object, object>来检索该属性。通过类似的方法,您可以返回强类型的Func<TObject, TResult>。对于任何给定的MethodInfo,如果您需要多次使用此调用,则应缓存其结果,因为此方法的开销至少比调用生成的委托大一个数量级。请注意保留HTML标记,但不要写解释。
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();
}

花了一些时间才弄清楚需要这个命名空间:using System.Linq.Expressions; - Crouching Kitten

6
在.NET 4.0中,您可以这样做,因为Func委托标记了TResult,其中包含out修饰符。.NET 3.5不支持通用协变/逆变,所以您无法简单地进行转换。我不确定是否有另一种比反射更快的聪明方法来完成它。
这是.NET 4.0 Func的文档页面链接。请注意,TResult标有“out”,因此其返回值可以被强制转换为较不具体的类型,例如对象。
以下代码是一个没有外部依赖项的快速示例,该示例在.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();
}

如果我的初始函数返回Guid之类的值类型时,当尝试强制转换为Func<int, object>时,我会得到运行时错误。 - John Bledsoe
是的,我也遇到了同样的问题。 - Aaron Stainback

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接