Faster way to cast a Func<T, T2> to Func<T, object>? [duplicate]
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
}
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.
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();
}
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();
}
精彩评论