Что значит выражение.Quote () сделайте это выражение.Константа() уже не может сделать?


примечание: Я знаю о более раннем вопросе "какова цель выражения LINQ.Метод цитирования?", а если Вы читаете на вы увидите, что это не ответ на мой вопрос.

я понимаю, в чем заявленная цель Expression.Quote() есть. Однако,Expression.Constant() может использоваться для той же цели (в дополнение ко всем целям, которые Expression.Constant() уже используется для). Поэтому я не понимаю, почему Expression.Quote() вообще не требуется.

чтобы продемонстрировать это, я написали быстрый пример, где обычно используется Quote (см. строку, помеченную восклицательными знаками), но я использовал Constant вместо этого, и он работал одинаково хорошо:

string[] array = { "one", "two", "three" };

// This example constructs an expression tree equivalent to the lambda:
// str => str.AsQueryable().Any(ch => ch == 'e')

Expression<Func<char, bool>> innerLambda = ch => ch == 'e';

var str = Expression.Parameter(typeof(string), "str");
var expr =
    Expression.Lambda<Func<string, bool>>(
        Expression.Call(typeof(Queryable), "Any", new Type[] { typeof(char) },
            Expression.Call(typeof(Queryable), "AsQueryable",
                            new Type[] { typeof(char) }, str),
            // !!!
            Expression.Constant(innerLambda)    // <--- !!!
        ),
        str
    );

// Works like a charm (prints one and three)
foreach (var str in array.AsQueryable().Where(expr))
    Console.WriteLine(str);

выход expr.ToString() то же самое для обоих, тоже (использую ли я Constant или Quote).

учитывая вышеизложенные наблюдения, похоже, что Expression.Quote() избыточна. Компилятор C# можно было бы сделать для компиляции вложенных лямбда-выражений в дерево выражений с участием Expression.Constant() вместо Expression.Quote(), и любой поставщик запросов LINQ, который хочет обрабатывать деревья выражений на каком-либо другом языке запросов (например, SQL), может искать ConstantExpression С типом Expression<TDelegate> вместо UnaryExpression специальные Quote тип узла, а все остальное будет то же самое.

чего мне не хватает? Почему было Expression.Quote() и специальный Quote тип узла для UnaryExpression придумали?

3 91

3 ответа:

короткий ответ:

оператор цитаты-это оператор, который индуцирует семантику замыкания на своем операнде. Константы-это просто значения.

кавычки и константы имеют разные смыслов и поэтому различные представления в дереве выражений. Иметь одно и то же представление для двух очень разных вещей-это очень сбивает с толку и ошибка склонны.

долго ответ:

рассмотрим следующее:

(int s)=>(int t)=>s+t

внешняя лямбда-это фабрика для сумматоров, привязанных к параметру внешней лямбды.

теперь предположим, что мы хотим представить это как дерево выражений, которое позже будет скомпилировано и выполнено. Каким должно быть тело дерева выражений? это зависит от того, хотите ли вы, чтобы скомпилированное состояние возвращало делегат или дерево выражений.

давайте начнем с увольнения неинтересный случай. Если мы хотим, чтобы он возвращал делегат, то вопрос о том, использовать ли цитату или константу, является спорным:

        var ps = Expression.Parameter(typeof(int), "s");
        var pt = Expression.Parameter(typeof(int), "t");
        var ex1 = Expression.Lambda(
                Expression.Lambda(
                    Expression.Add(ps, pt),
                pt),
            ps);

        var f1a = (Func<int, Func<int, int>>) ex1.Compile();
        var f1b = f1a(100);
        Console.WriteLine(f1b(123));

лямбда имеет вложенную лямбду; компилятор генерирует внутреннюю лямбду как делегат функции, замкнутой над состоянием функции, сгенерированной для внешней лямбды. Нам больше не нужно рассматривать это дело.

предположим, что мы хотим, чтобы скомпилированное состояние возвращало выражение дерево внутренних дел. Есть два способа для этого есть легкий путь и трудный путь.

трудный путь состоит в том, чтобы сказать, что вместо

(int s)=>(int t)=>s+t

на самом деле мы имеем в виду

(int s)=>Expression.Lambda(Expression.Add(...

а затем сгенерируйте дерево выражений для это, производства этот бардак:

        Expression.Lambda(
            Expression.Call(typeof(Expression).GetMethod("Lambda", ...

бла - бла-бла, десятки строк кода отражения, чтобы сделать лямбда. цель оператора quote-сообщить компилятору дерева выражений, что мы хотим получить заданную лямбду обрабатывается как дерево выражений, а не как функция, без необходимости явно генерировать код генерации дерева выражений.

самый простой способ это:

        var ex2 = Expression.Lambda(
            Expression.Quote(
                Expression.Lambda(
                    Expression.Add(ps, pt),
                pt)),
            ps);

        var f2a = (Func<int, Expression<Func<int, int>>>)ex2.Compile();
        var f2b = f2a(200).Compile();
        Console.WriteLine(f2b(123));

и действительно, если вы скомпилируете и запустите этот код, вы получите правильный ответ.

обратите внимание, что оператор quote-это оператор, который индуцирует семантику замыкания на внутренней лямбде, которая использует внешнюю переменную, формальный параметр внешней лямбды.

вопрос: почему бы не исключить цитату и сделать это сделать то же самое?

        var ex3 = Expression.Lambda(
            Expression.Constant(
                Expression.Lambda(
                    Expression.Add(ps, pt),
                pt)),
            ps);

        var f3a = (Func<int, Expression<Func<int, int>>>)ex3.Compile();
        var f3b = f3a(300).Compile();
        Console.WriteLine(f3b(123));

константа не индуцирует семантику замыкания. С чего бы это? Вы сказали, что это было постоянный. Это просто ценность. Он должен быть совершенным, как передано компилятору; компилятор должен быть в состоянии просто генерировать дамп этого значения в стек, где это необходимо.

поскольку нет никакого закрытия индуцированного, если вы сделаете это, вы получите "переменную' s 'типа' System. Int32 ' нет определено " исключение при вызове.

(в сторону: я только что рассмотрел генератор кода для создания делегата из цитируемых деревьев выражений, и, к сожалению, комментарий, который я поместил в код еще в 2006 году, все еще существует. К вашему сведению, поднятый внешний параметр snapshotted в константу, когда дерево выражений в кавычках реифицируется как делегат компилятором среды выполнения. Была веская причина, почему я написал код таким образом, который я не помню точно момент, но у него есть неприятный побочный эффект введения закрытия значения внешних параметров, а не за закрытие переменные. По-видимому, команда, которая унаследовала этот код, решила не исправлять этот недостаток, поэтому, если вы полагаетесь на мутацию закрытого внешнего параметра, наблюдаемого в скомпилированной цитируемой внутренней лямбде, вы будете разочарованы. Однако, поскольку это довольно плохая практика программирования, чтобы оба (1) мутировать формальный параметр и (2) полагайтесь на мутацию внешней переменной, я бы рекомендовал вам изменить свою программу, чтобы не использовать эти две плохие практики программирования, а не ждать исправления, которое, похоже, не ожидается. Прошу прощения за ошибку.)

Итак, повторяю вопрос:

компилятор C# можно было бы сделать для компиляции вложенных лямбда-выражений в дерево выражений, включающее выражение.Константа () вместо выражения.Quote () и любой запрос LINQ поставщик, который хочет обрабатывать деревья выражений на каком-либо другом языке запросов (например, SQL), может искать константу с выражением типа вместо UnaryExpression со специальным типом узла Quote, и все остальное будет таким же.

вы правы. Мы может кодирует семантическую информацию, которая означает "индуцировать семантику замыкания на этом значении" с помощью использование типа константного выражения в качестве флага.

"константа" будет иметь значение " использовать это постоянное значение,если тип оказывается типом дерева выражений и значение является допустимым деревом выражений, и в этом случае вместо этого используйте значение, которое является деревом выражений, полученным в результате перезаписи внутренней части данного дерева выражений, чтобы вызвать семантику замыкания в контексте любых внешних лямбд, в которых мы можем находиться прямо сейчас.

а зачем б мы делаем эта сумасшедшая штука? оператор цитаты-это безумно сложный оператор, и он должен быть использован явно если вы собираетесь использовать его. Вы предполагаете, что для того, чтобы быть экономным, не добавляя один дополнительный метод фабрики и тип узла среди нескольких десятков уже существующих, мы добавляем причудливый угловой случай к константам, так что константы иногда являются логически постоянными, а иногда они переписываются лямбды с закрытием семантика.

это также будет иметь несколько странный эффект, что константа не означает "использовать это значение". Предположим, по какой-то странной причине вы хотел третий случай выше, чтобы скомпилировать дерево выражений в делегат, который выдает дерево выражений, которое имеет не перезаписанную ссылку на внешнюю переменную? Зачем? Возможно, потому что вы тестируете свой компилятор и хотите просто передать константу, чтобы вы могли выполнить какой-то другой анализ это потом. Ваше предложение сделает это невозможным; любая константа, которая имеет тип дерева выражений, будет переписана независимо. Есть разумное ожидание, что" константа "означает"использовать это значение". "Константа" - это узел "делай, что я говорю". Задача постоянного процессора-не догадываться о том, что вы означает сказать на основе типа.

и обратите внимание, конечно, что вы сейчас кладете бремя понимания (то есть понимание того, что константа имеет сложная семантика, которая означает " константа "в одном случае и" индуцирует семантику замыкания " на основе флага, который в системы типа) на каждый поставщик, который выполняет семантический анализ дерева выражений, а не только на поставщиках Microsoft. сколько из этих сторонних поставщиков получат это неправильно?

"цитата" размахивает большим красным флагом, который говорит: "Эй, приятель, посмотри сюда, я вложенное лямбда-выражение, и у меня есть дурацкий семантика, если я закрыт над внешней переменной!"в то время как" константа "говорит:" Я не более чем ценность; используйте меня так, как считаете нужным."Когда что-то сложно и опасно, мы хотим, чтобы он размахивал красными флагами, не скрывая этого факта, заставляя пользователя копаться в система типа для того чтобы выяснить, является ли это значение или нет.

кроме того, идея о том, что избегание избыточности является даже целью, неверна. Конечно, избегая ненужного, сбивающая с толку избыточность-это цель, но большая часть избыточности-это хорошо; избыточность создает ясность. Новые заводские методы и виды узлов дешевые. Мы можем сделать столько, сколько нам нужно, чтобы каждый из них представлял одну операцию чисто. Нам не нужно прибегать к таким неприятным трюкам, как "это означает одно, если это поле не настроено на эту вещь, и в этом случае это означает что-то еще."

на этот вопрос уже получен отличный ответ. Я также хотел бы указать на ресурс, который может оказаться полезным с вопросами о деревьях выражений:

существует проект CodePlex от Microsoft под названием Динамическая Языковая Среда Выполнения. Его документация включает в себя документ под названием, "Expression Trees V2 Spec", что именно: спецификация для деревьев выражений LINQ в .NET 4.

например, говорит следующее о Expression.Quote:

4.4.42 цитата

используйте Quote в UnaryExpressions для представления выражения, которое имеет "постоянное" значение типа Expression. В отличие от узла константы, узел кавычки специально обрабатывает содержащиеся узлы ParameterExpression. Если содержащийся узел ParameterExpression объявляет локальный узел, который будет закрыт в результирующем выражении, то Quote заменяет ParameterExpression в его опорных местах. Во время выполнения, когда вычисляется узел Quote, он заменяет ссылки на переменные закрытия ссылочными узлами ParameterExpression, а затем возвращает выражение в кавычках. [...] (стр. 63-64)

Я думаю, дело здесь в выразительности дерева. Константное выражение, содержащее делегат, на самом деле просто содержит объект, который является делегатом. Это менее выразительно, чем прямая разбивка на унарное и двоичное выражение.