Более быстрый способ передать Func <T, T2> в Func <T, object>?

Есть ли более быстрый способ отбрасывать 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> без кода отражения в возвращенной лямбда, как в примере выше?

EDIT: добавлено модифицированное решение

Хорошо благодаря 280Z28 для того, чтобы вести меня по правильному пути, который я включил в окончательное решение ниже. Я оставил код отражения там для платформ, которые не поддерживают выражения. Для платформ, которые делают это, демонстрируют увеличение 26x до 27x (13/.5 тиков) для получения свойств int и string.

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
}

Ответ 1

Как вы знаете, вы можете получить 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();
}

Ответ 2

В .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();
}

Ответ 3

Рассматривали ли вы следующее:

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

Таким образом вы просто завершаете делегат.