Этот вопрос уже имеет ответ здесь:
Есть ли более быстрый способ бросить 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
}
Как вы знаете, вы можете получить 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();
}
Вы подумали о том, чтобы сделать следующее:
Func<Foo, Bar> typed = (f) => return new Bar();
Func<Foo, object> untyped = (f) => typed(f);
Таким образом, вы просто завернете делегата.
В .NET 4.0 Вы можете сделать это, потому что Func-делегат отмечает Tresult с модификатором OUT. .Net 3.5 не поддерживает общие ковариации / контравариационные , поэтому вы не можете просто бросить. Я не уверен, что есть еще один умный способ сделать это, это быстрее, чем отражение.
Вот страница .NET 4.0 DOC для Func . Обратите внимание, что Tresult помечается «OUT», поэтому его возвращаемое значение может быть отличным в качестве менее конкретного типа, такого как объект.
// 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();
}