【问题标题】:Expression Tree. Storing LambdaExpression with runtime types in Expression.Variable表达式树。在 Expression.Variable 中存储具有运行时类型的 LambdaExpression
【发布时间】:2018-04-06 11:21:49
【问题描述】:

我正在尝试从不同的结构构建表达式树。为方便起见,我们称该结构为 S。我遇到了一个问题。我必须能够从 S 中的图形并基于 S 指定的输入和输出类型构造一个LambdaExpression。 S 指定表达式本身和LambdaExpression 的签名(例如Func<int, int>, Func<bool, string, int> 等)。因为我可以在构建表达式时使用非泛型 Expression.Lambda(...),所以我可以放心地忽略签名。但是当表达式有递归调用时它不起作用。我必须事先将表达式本身存储在Expression.Variable(typeof(Func<...>)) 中以供将来调用。但是我不知道编译时的类型,因为我将 S 作为数据,这完全是运行时的事情。

示例如下:

public class Structure
{
    public Parameter[] InputParams { get; set; }
    public Parameter OutputParam { get; set; }
    public Graph Expression { get; set; }
}
public class Parameter
{
    public string Name { get; set; }
    public string Type { get; set; }
}
public class Builder
{
    public static Expression Build(Structure s)
    {
        var inputParams = new ParameterExpression[s.InputParams.Length];
        int index = 0;
        foreach (var param in s.InputParams)
        {
            inputParams[index++] = Expression.Parameter(Utils.GetDataType(param.Type), param.Name);
        }

        ParameterExpression delegVar = Expression.Variable(typeof(Func<int, int>), "sum"); // here types of Func must be taken from S

        LambdaExpression expression = Expression.Lambda(
            Expression.Block(
                new[] { delegVar },
                Expression.Assign(delegVar,
                    Expression.Lambda(
                        Expression.Block(
                            // some work is done here
                            Expression.Invoke(delegVar, inputParams)
                        ),
                        inputParams
                    )
                ),
                Expression.Invoke(delegVar, inputParams)
            ),
            inputParams
        );

        return expression;
    }
}

请忽略表达式会在运行时抛出 StackOverflowException 的事实。

【问题讨论】:

  • 你能否展示一个如何初始化结构的例子?举例说明如何调用 Build() 方法。
  • 感谢您的回复。但我已经解决了这个问题。我会在这里发布。

标签: c# linq lambda expression-trees


【解决方案1】:

在互联网上广泛搜索后,我终于能够解决问题。我所做的是我创建了一个类型定义,并通过传递类型参数从中创建了一个泛型类型。这是解决方案。

public class Structure
{
    public Parameter[] InputParams { get; set; }
    public Parameter OutputParam { get; set; }
    public Graph Expression { get; set; }
}
public class Parameter
{
    public string Name { get; set; }
    public string Type { get; set; }
}
public class Builder
{
    public static Expression Build(Structure s)
    {
        var inputParams = new ParameterExpression[s.InputParams.Length];
        int index = 0;
        foreach (var param in s.InputParams)
        {
            inputParams[index++] = Expression.Parameter(GetDataType(param.Type), param.Name);
        }

        Type lambdaDefinition = GetLambdaTypeDefinition(s.InputParams.Length);
        Type[] typeArgs = s.InputParams.Select(p => GetDataType(p.Type)).Concat(new Type[] { GetDataType(s.OutputParam.Type) }).ToArray();
        Type lambdaType = lambdaDefinition.MakeGenericType(typeArgs);

        ParameterExpression delegVar = Expression.Variable(lambdaType, "sum");

        LambdaExpression expression = Expression.Lambda(
            Expression.Block(
                new[] { delegVar },
                Expression.Assign(delegVar,
                    Expression.Lambda(
                        Expression.Block(
                            // some work is done here
                            Expression.Invoke(delegVar, inputParams)
                        ),
                        inputParams
                    )
                ),
                Expression.Invoke(delegVar, inputParams)
            ),
            inputParams
        );

        return expression;
    }

    private static Type GetDataType(string type)
    {
        switch (type)
        {
            case DataTypes.Boolean:
                return typeof(bool);
            case DataTypes.Number:
                return typeof(double);
            case DataTypes.NumArray:
                return typeof(double[]);
        }
        return null;
    }

    private static Type GetLambdaTypeDefinition(int inputLength)
    {
        switch (inputLength)
        {
            case 1:
                return typeof(Func<,>);
            case 2:
                return typeof(Func<,,>);
            case 3:
                return typeof(Func<,,,>);
            case 4:
                return typeof(Func<,,,,>);
            case 5:
                return typeof(Func<,,,,,>);
            case 6:
                return typeof(Func<,,,,,,>);
            case 7:
                return typeof(Func<,,,,,,,>);
            case 8:
                return typeof(Func<,,,,,,,,>);
            case 9:
                return typeof(Func<,,,,,,,,,>);
            case 10:
                return typeof(Func<,,,,,,,,,,>);
            case 11:
                return typeof(Func<,,,,,,,,,,,>);
            case 12:
                return typeof(Func<,,,,,,,,,,,,>);
            case 13:
                return typeof(Func<,,,,,,,,,,,,,>);
            case 14:
                return typeof(Func<,,,,,,,,,,,,,,>);
            case 15:
                return typeof(Func<,,,,,,,,,,,,,,,>);
            case 16:
                return typeof(Func<,,,,,,,,,,,,,,,,>);
            default:
                throw new Exception("Too many input parameters");
        }
    }
}

编辑:我的解决方案很糟糕。正如 Ivan Stoev 提到的,有一种方法正是为此目的。我是Expression.GetFuncType

【讨论】:

  • 真的是你的问题吗? Expression.GetFuncType
  • 哦,那好多了!非常感谢!愚蠢的我,我应该在搜索答案之前先阅读文档。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-04
  • 1970-01-01
  • 1970-01-01
  • 2013-06-06
  • 1970-01-01
  • 1970-01-01
  • 2011-09-26
相关资源
最近更新 更多