更快地转换 Func的方法到 Func?

发布于 2024-08-19 21:30:02 字数 2172 浏览 5 评论 0原文

是否有更快的方法将 Fun 转换为 Func

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 在返回的 lambda 中没有反射代码(如上面的示例所示)?

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

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

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
}

Is there a faster way to cast Fun<TEntity, TId> to 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?
 }
}

Is there a way to convert typedGetPropertyFn to a Func<TEntity, object> without having reflection code in the returned lambda like the example above?

EDIT: added modified solution

Ok thanks to 280Z28 for leading me down the right path which I've included in the final solution below. I've left the reflection code in there for platforms that don't support Expressions. For platforms that do it's showing a 26x to 27x (13 / .5 ticks avg) perf increase for getting int and string properties.

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
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

帥小哥 2024-08-26 21:30:02

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

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

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

Have you considered doing the following:

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

This way you just wrap the delegate.

南街女流氓 2024-08-26 21:30:02

如您所知,您可以从 PropertyInfo.GetGetMethod() 获取 MethodInfo。由此,您可以使用以下代码获取 Func 来检索该属性。通过类似的方法,您可以返回强类型 Func。对于任何给定的 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();
}

As you know, you can obtain a MethodInfo from PropertyInfo.GetGetMethod(). From that, you can use the following to get a Func<object, object> to retrieve that property. By a similar method, you could return a strongly-typed Func<TObject, TResult>. For any given MethodInfo, you should cache the results of this call if you need it more than once since this method is at least an order of magnitude more expensive than calling the resulting delegate.

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();
}
み零 2024-08-26 21:30:02

在 .NET 4.0 中,您可以执行此操作,因为 Func 委托使用 out 修饰符标记 TResult。 .NET 3.5 不支持通用协方差/逆变所以你不能简单地投射。我不确定是否还有另一种比反射更快的聪明方法。

这是Func 的 .NET 4.0 文档页面。请注意,TResult 被标记为“out”,因此它的返回值可以转换为不太特定的类型,例如对象。


For a quick example that has no external dependencies, the following code fails to compile on .NET 3.5 but compiles and runs correctly on .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();
}

In .NET 4.0 you can do this because the Func delegate marks TResult with the out modifier. .NET 3.5 does not support generic covariance/contravariance so you can't simply cast. I'm not sure if there's another clever way of doing it that is faster than reflection.

Here's the .NET 4.0 doc page for Func. Notice that TResult is marked with "out" so its return value can be cast as a less-specific type such as object.


For a quick example that has no external dependencies, the following code fails to compile on .NET 3.5 but compiles and runs correctly on .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();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文