Короткий ответ:
Оператор кавычек - это оператор, который индуцирует семантику закрытия для своего операнда. Константы - это просто значения.
Кавычки и константы имеют разные значения и поэтому имеют разные представления в дереве выражений. Наличие одного и того же представления для двух очень разных вещей чрезвычайно сбивает с толку и чревато ошибками.
Длинный ответ:
Учтите следующее:
(int s)=>(int t)=>s+t
Внешняя лямбда - это фабрика для сумматоров, привязанных к параметру внешней лямбды.
Теперь предположим, что мы хотим представить это как дерево выражений, которое позже будет скомпилировано и выполнено. Каким должно быть тело дерева выражений? Это зависит от того, хотите ли вы, чтобы скомпилированное состояние возвращало делегат или дерево выражений.
Начнем с неинтересного случая. Если мы хотим, чтобы он возвращал делегата, то вопрос о том, использовать ли Quote или Constant, является спорным:
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", ...
бла-бла-бла, десятки строк кода отражения, чтобы сделать лямбду. Цель оператора кавычки - сообщить компилятору дерева выражений, что мы хотим, чтобы данная лямбда обрабатывалась как дерево выражения, а не как функция, без необходимости явно генерировать код генерации дерева выражений.
Самый простой способ:
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 году, все еще там. К вашему сведению, поднятый внешний параметр снят em > в константу, когда цитируемое дерево выражений реифицируется как делегат компилятором среды выполнения. Я написал код таким способом, который я не помню в данный момент, но у него есть неприятный побочный эффект: введение закрытия по значениям внешних параметров, а не по переменным. Очевидно, команда, унаследовавшая этот код, решила не исправлять этот недостаток, поэтому, если вы полагаетесь на мутацию закрытый внешний параметр, наблюдаемый в скомпилированной цитируемой внутренней лямбде, вы будете разочарованы. Однако, поскольку это довольно плохая практика программирования: (1) изменять формальный параметр и (2) полагаться на мутацию внешняя переменная, я бы порекомендовал вам изменить свою программу чтобы не использовать эти две плохие практики программирования, а не ждать исправления, которое, похоже, не ожидается. Приносим извинения за ошибку.)
Итак, повторим вопрос:
Компилятор C # можно было заставить компилировать вложенные лямбда-выражения в дерево выражений с использованием Expression.Constant () вместо Expression.Quote () и любого поставщика запросов LINQ, который хочет обрабатывать деревья выражений на каком-либо другом языке запросов (например, SQL ) может искать ConstantExpression с типом Expression вместо UnaryExpression со специальным типом узла Quote, а все остальное будет таким же.
Ты прав. Мы могли закодировать семантическую информацию, которая означает «вызвать семантику закрытия для этого значения», используя тип константного выражения в качестве флага.
Тогда "константа" будет иметь значение "использовать это постоянное значение, если тип не является типом дерева выражения и значение является допустимым деревом выражения, и в этом случае , вместо этого используйте значение, которое является деревом выражений, полученным в результате переписывания внутренней части данного дерева выражений, чтобы вызвать семантику закрытия в контексте любых внешних лямбда-выражений, в которых мы могли бы находиться прямо сейчас.
Но зачем нам поступать так безумно? Оператор кавычек - безумно сложный оператор, и его следует использовать явно, если вы собираетесь его использовать. Вы предлагаете, чтобы не добавлять один дополнительный фабричный метод и тип узла среди нескольких десятков уже имеющихся, чтобы мы добавили к константам причудливый угловой регистр, так что константы иногда являются логическими константами, а иногда их переписывают. лямбды с семантикой закрытия.
Это также будет иметь несколько странный эффект, поскольку константа не означает «использовать это значение». Предположим, по какой-то причудливой причине вы захотели, что в третьем случае выше скомпилировали дерево выражений в делегат, который выдает дерево выражений, имеющее непереписанную ссылку на внешнюю переменную? Почему? Возможно, потому, что вы тестируете свой компилятор и хотите просто передать константу, чтобы вы могли позже выполнить другой анализ. Ваше предложение сделало бы это невозможным; любая константа, которая относится к типу дерева выражений, будет перезаписана независимо. Есть разумные основания полагать, что «константа» означает «использовать это значение». «Константа» - это узел «делай, что я говорю». Задача процессора констант не состоит в том, чтобы угадать, что вы имели в виду, исходя из типа.
И, конечно же, обратите внимание, что теперь вы возлагаете бремя понимания (то есть понимание того, что константа имеет сложную семантику, которая означает «константа» в одном случае и «вызывает семантику закрытия» на основе флага, который в системе типов равен ) для каждого поставщика, который выполняет семантический анализ дерева выражений, а не только для поставщиков Microsoft. Сколько сторонних поставщиков ошиблись бы?
«Цитата» размахивает большим красным флагом, который говорит: «Эй, приятель, посмотри сюда, я вложенное лямбда-выражение, и у меня дурацкая семантика, если я закрываюсь по внешней переменной!» тогда как «Константа» говорит: «Я не более чем ценность; используйте меня, как считаете нужным». Когда что-то сложное и опасное, мы хотим, чтобы оно показывало красные флажки, а не скрывать этот факт, заставляя пользователя копаться в системе типов, чтобы выяснить, является ли это значение особенным или нет. .
Более того, идея о том, что избегание избыточности является даже целью, неверна. Конечно, целью является предотвращение ненужной, сбивающей с толку избыточности, но большая часть избыточности - это хорошо; избыточность создает ясность. Новые фабричные методы и типы узлов дешевы. Мы можем сделать столько, сколько нам нужно, чтобы каждая из них четко представляла одну операцию. Нам не нужно прибегать к неприятным уловкам вроде «это означает одно, если в этом поле не установлено это значение, и в этом случае это означает что-то другое».
person
Eric Lippert
schedule
20.09.2010