使用表达式来“广播” Func<对象,对象>。到Func< T,TRet> [英] Using Expression to 'Cast' Func<object, object> to Func<T, TRet>
问题描述
我编写了一个小函数,试图动态地执行以下操作:
I've written a little function that attempts to do the following dynamically:
Func<object, object> fa = i => Convert.ChangeType(i, typeof (string));
Func<int, string> fb = o => (string) fa((int)o);
函数如下:
/// <summary>
/// Converts <see cref="Func{object, object}" /> to <see cref="Func{T, TResult}" />.
/// </summary>
public static Delegate Convert(Func<object, object> func, Type argType, Type resultType)
{
Contract.Requires(func != null);
Contract.Requires(resultType != null);
var param = Expression.Parameter(argType);
var converted = Expression.Convert(
Expression.Call(func.Method, Expression.Convert(param, typeof (object))),
resultType);
var delegateType = typeof (Func<,>).MakeGenericType(argType, resultType);
return Expression.Lambda(delegateType, converted, param).Compile();
}
现在,当不涉及闭包时,此方法可以正常工作-此测试通过: / p>
Now this works ok when there is no closure involved - this test passes:
[Test]
public void When_Converting_Without_Closure_Then_Suceeds()
{
// Arrange
Func<object, object> f = i => Convert.ChangeType(i, typeof(string));
var sut = FuncConversion.Convert(f, typeof(int), typeof(string));
// Act
var res = (string) sut.DynamicInvoke(10);
// Assert
Assert.AreEqual(typeof(Func<int, string>), sut.GetType());
Assert.AreEqual("10", res);
}
但是当涉及到闭包时,此测试将失败:
but when a closure is involved, this test fails:
[Test]
public void When_Converting_With_Closure_Then_Succeeds()
{
// Arrange
var typeTo = typeof (string);
Func<object, object> f = i => Convert.ChangeType(i, typeTo);
var sut = FuncConversion.Convert(f, typeof(int), typeof(string));
// Act
var res = (string)sut.DynamicInvoke(10);
// Assert
Assert.AreEqual(typeof(Func<int, string>), sut.GetType());
Assert.AreEqual("10", res);
}
System.ArgumentException:静态方法需要null实例,非静态方法需要non -null实例。
参数名称:System.Linq.Expressions.Expression.ValidateStaticOrInstanceMethod(Expressioninstance,MethodInfo方法)中的方法
System.Linq.Expressions.Expression.Call(MethodInfo方法,Expressionarg0)中的方法
System.ArgumentException : Static method requires null instance, non-static method requires non-null instance. Parameter name: method at System.Linq.Expressions.Expression.ValidateStaticOrInstanceMethod(Expression instance, MethodInfo method) at System.Linq.Expressions.Expression.Call(MethodInfo method, Expression arg0)
有什么问题吗?
推荐答案
确定。问题在于,对于闭包,通常是静态方法的func具有其第一个参数,该参数将是实例方法上的目标实例,用于保持闭包状态。因此,我需要检查该状态是否存在,并使用它进行调用。
Ok fixed it. The problem is that with a closure, a func which is normally a static method, has its first param, which would be the target instance on an instance method, used to hold the closure state. So I need to check if that state is there and invoke with it if it is.
Et voila:
/// <summary>
/// Converts <see cref="Func{object, object}" /> to <see cref="Func{T, TResult}" />.
/// </summary>
public static Delegate Convert(Func<object, object> func, Type argType, Type resultType)
{
// If we need more versions of func then consider using params Type as we can abstract some of the
// conversion then.
Contract.Requires(func != null);
Contract.Requires(resultType != null);
var param = Expression.Parameter(argType);
var convertedParam = new Expression[] {Expression.Convert(param, typeof (object))};
// This is gnarly... If a func contains a closure, then even though its static, its first
// param is used to carry the closure, so its as if it is not a static method, so we need
// to check for that param and call the func with it if it has one...
Expression call;
call = Expression.Convert(
func.Target == null
? Expression.Call(func.Method, convertedParam)
: Expression.Call(Expression.Constant(func.Target), func.Method, convertedParam), resultType);
var delegateType = typeof (Func<,>).MakeGenericType(argType, resultType);
return Expression.Lambda(delegateType, call, param).Compile();
}
这篇关于使用表达式来“广播” Func<对象,对象>。到Func< T,TRet>的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!