将Func <t,t2 =“”>强制转换为Func <t,object =“”>?</t,> </t,>

时间:2010-01-30 19:10:46

标签: c# reflection lambda covariance

是否有更快捷的方式将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属性,平台显示 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
}

3 个答案:

答案 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);

这样你就可以包装委托了。