Более быстрый способ бросить Func <T, T2> в Func <T, объект>? [дубликат]

Этот вопрос уже имеет ответ здесь:

Есть ли более быстрый способ бросить Fun к Func

public static class StaticAccessors
{
 public static Func TypedGetPropertyFn(PropertyInfo pi)
 {
  var mi = pi.GetGetMethod();
  return (Func)Delegate.CreateDelegate(typeof(Func), mi);
 }

 public static Func ValueUnTypedGetPropertyTypeFn(PropertyInfo pi)
 {
  var mi = typeof(StaticAccessors).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 к a Func не имея отражательного кода в возвращенной лямбде как пример выше?

Править: добавленное измененное решение

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

public static Func ValueUnTypedGetPropertyTypeFn(PropertyInfo pi)
{
    var mi = typeof(StaticAccessors).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> (
            Expression.Convert(
                Expression.Call(
                    Expression.Convert(obj, typedMi.DeclaringType),
                    typedMi
                ),
                typeof(object)
            ),
            obj
        );
    return expr.Compile();
    #endif
}
17
задан mythz 30 January 2010 в 20:58
поделиться

3 ответа

Как вы знаете, вы можете получить MethodInfo из PropertyInfo.getgetMethod () . Из этого вы можете использовать следующее, чтобы получить функцию 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();
}
8
ответ дан 30 November 2019 в 13:40
поделиться

Вы подумали о том, чтобы сделать следующее:

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

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

8
ответ дан 30 November 2019 в 13:40
поделиться

В .NET 4.0 Вы можете сделать это, потому что Func-делегат отмечает Tresult с модификатором OUT. .Net 3.5 не поддерживает общие ковариации / контравариационные , поэтому вы не можете просто бросить. Я не уверен, что есть еще один умный способ сделать это, это быстрее, чем отражение.

Вот страница .NET 4.0 DOC для 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();
}
5
ответ дан 30 November 2019 в 13:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: