c# - 使用表达式将 'Cast' Func<object, object> 转换为 Func<T, TRet>

标签 c# closures expression

我编写了一个小函数,尝试动态执行以下操作:

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

现在,当不涉及闭包时,这可以正常工作 - 该测试通过:

    [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);
    }

但是当涉及到闭包时,这个测试就会失败:

    [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 实例,非静态方法需要非 null 实例。 参数名称:方​​法 在 System.Linq.Expressions.Expression.ValidateStaticOrInstanceMethod(表达式实例,MethodInfo 方法) 在 System.Linq.Expressions.Expression.Call(MethodInfo 方法,表达式 arg0)

知道出了什么问题吗?

最佳答案

好的,修复了。问题是,对于闭包,通常是静态方法的 func 有其第一个参数,该参数将是实例方法上的目标实例,用于保存闭包状态。所以我需要检查该状态是否存在,如果存在则调用它。

瞧:

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

关于c# - 使用表达式将 'Cast' Func<object, object> 转换为 Func<T, TRet>,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16590685/

相关文章:

c# - Web API 和 Angular 5 token 身份验证 - 登录后获取用户信息

c# - "Could not find endpoint element with name..."

c# - 发送到 Azure 事件中心错误

Swift - 如何使用闭包在 View 模型中触发函数?

javascript - 为无父本地变量 : impossible? 定义 Setter/Getter

c - C代码错误: expression is not assignable错误

c# - 创建、组合和缓存 lambda 表达式

c# - 获取无线网络强度

php - 带有递归的 ArrayCollection::forAll

javascript - Angular JS 中的 jQuery 表达式等效项