Как объединить результат нескольких выражений Linq в одно выражение?

У меня есть список выражений Linq List<Expression>, где каждый тип выражения (тип, который будет возвращать выражение) имеет значение Item или Item[].

Я пытаюсь написать код, который будет принимать упомянутую коллекцию в качестве входного параметра и создавать выражение Linq, которое будет возвращать один список (или массив) элементов (Item[]).

Вот абстрактный пример:

public static string[] GetStrings()
{
    return new[]
        {
            "first",
            "second",
            "third"
        };
}

public static string GetString()
{
    return "single1";
}

private void SOExample()
{
    var expressions = new List<Expression>
    {
        Expression.Call(GetType().GetMethod("GetString")),
        Expression.Call(GetType().GetMethod("GetStrings")), 
        Expression.Call(GetType().GetMethod("GetString")),
        Expression.Call(GetType().GetMethod("GetStrings"))
    };

    // some magic code here
    var combined = SomeMagicHere(expressions);
}


private Expression SomeMagicHere(List<Expression> expressions)
{
    foreach (var expression in expressions)
    {
        if (expression.Type.IsArray)
        {
            // Use array's elements
        } 
        else
        {
            // Use expression
        }
    }

То, что я пытаюсь сделать, это создать одно выражение, которое вернет список Item (строки в моем примере) из предоставленного списка.


person Hubert R. Skrzypek    schedule 08.10.2012    source источник
comment
выражение редко используется само по себе и еще реже, когда не является частью LambdaExpression. Есть ли конкретная причина, по которой здесь используется Expression, а не... ну, может быть, просто отражение, может быть, делегаты и т. д. Expression здесь не кажется очевидным выбором. Также: что вы ищете в результате: вы хотите, чтобы он оценивал методы, объединяя их результаты? или построить Expression, который при компиляции и т. д. будет оценивать дерево? или...? в основном: как бы выглядел ваш идеальный результат здесь? Также: что является целевым абонентом? (важно: абонентам ORM это не понравится)   -  person Marc Gravell    schedule 08.10.2012


Ответы (1)


Это кажется очень странным сценарием, и в большинстве случаев я ожидаю увидеть здесь использование необработанного отражения (или, возможно, делегатов), а не Expression — это не совсем подходит. Но: вы можете сделать это, превратив выражения в блок, каждый из которых вызывает Add или AddRange для добавления значений в список. Например:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

static class Program
{
    public static string GetString()
    {
        return "single1";
    }
    public static string[] GetStrings()
    {
        return new[]
        {
            "first",
            "second",
            "third"
        };
    }
    static void Main()
    {
        var expressions = new List<Expression>
        {
            Expression.Call(typeof(Program).GetMethod("GetString")),
            Expression.Call(typeof(Program).GetMethod("GetStrings")), 
            Expression.Call(typeof(Program).GetMethod("GetString")),
            Expression.Call(typeof(Program).GetMethod("GetStrings"))
        };

        // some magic code here
        var combined = SomeMagicHere(expressions);

        // show it works
        var lambda = Expression.Lambda<Func<List<string>>>(combined);
        var list = lambda.Compile()();
    }
    private static Expression SomeMagicHere(List<Expression> expressions)
    {
        List<Expression> blockContents = new List<Expression>();
        var var = Expression.Variable(typeof(List<string>), "list");
        blockContents.Add(Expression.Assign(var,
            Expression.New(typeof(List<string>))));
        foreach (var expression in expressions)
        {
            if (expression.Type.IsArray)
            {
                blockContents.Add(Expression.Call(
                    var, var.Type.GetMethod("AddRange"), expression));
            }
            else
            {
                blockContents.Add(Expression.Call(var,
                    var.Type.GetMethod("Add"), expression));
            }
        }
        blockContents.Add(var); // last statement in a block is the effective
                                // value of the block
        return Expression.Block(new[] {var}, blockContents);
    }

}
person Marc Gravell    schedule 08.10.2012
comment
Привет Марк, спасибо за ваш ответ - это именно то, что я искал. Я создаю это выражение как часть большего выражения, которое в конечном итоге компилируется и выполняется. Код используется внутри собственного языкового компилятора :) - person Hubert R. Skrzypek; 08.10.2012
comment
@Hubert Должно быть, я сумасшедший, но когда мне это нужно, я просто делаю это прямо на ILGenerator ;p - person Marc Gravell; 08.10.2012
comment
Идея использования Linq Expressions не моя, мне пришлось следовать тому, что я нашел (ANTLR + Linq Expressions) :) В любом случае, большое спасибо, я чувствую, что мне нужно больше разбираться в Linq Expressions :) - person Hubert R. Skrzypek; 08.10.2012