Краткий ответ:
Оператор кавычки - это оператор , который вызывает семантику замыкания в своем операнде . Константы - это просто значения.
Кавычки и константы имеют различные значения и, следовательно, имеют различные представления в дереве выражений . Наличие одного и того же представления для двух совершенно разных вещей крайне сбивает с толку и подвержено ошибкам.
Длинный ответ:
Рассмотрим следующее:
(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(...
И затем сгенерировать дерево выражений для , которое , создавая this mess * :
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));
Константа не вызывает семантику замыкания. Зачем это? Вы сказали, что это была константа . Это просто ценность. Это должно быть идеально, как передано компилятору; компилятор должен иметь возможность просто генерировать дамп этого значения в стек, где это необходимо.
Так как замыкание не вызывается, если вы сделаете это, вы получите исключение «переменная» типа «System.Int32» не определено »при вызове.
(Помимо: я только что рассмотрел генератор кода для создания делегатов из деревьев выражений в кавычках, и, к сожалению, комментарий, который я вставил в код еще в 2006 году, все еще там. К вашему сведению, внешний параметр hoisted равен snapshotted в константу, когда дерево выражений в кавычках преобразуется в качестве делегата компилятором времени выполнения. Была веская причина, почему я написал код таким способом, который я не помню в данный момент, но у него есть неприятная сторонаэффект введения замыкания по значениям внешних параметров, а не замыкания по переменным . Очевидно, команда, унаследовавшая этот код, решила не исправлять этот недостаток, поэтому, если вы полагаетесь на мутациюЗамкнутый внешний параметр, наблюдаемый в скомпилированной цитируемой внутренней лямбде, вы будете разочарованы, однако, поскольку это довольно плохая практика программирования - и (1) изменять формальный параметр, и (2) полагаться на мутациювнешняя переменная, я бы порекомендовал вам чанПусть ваша программа не использует эти две плохие практики программирования, вместо того, чтобы ждать исправления, которое, похоже, не ожидается.Извиняюсь за ошибку.)
Итак, чтобы повторить вопрос:
Компилятор C # мог быть создан для компиляции вложенных лямбда-выражений в дерево выражений, включающее Expression.Constant ()вместо Expression.Quote (), и любой поставщик запросов LINQ, который хочет обрабатывать деревья выражений на каком-либо другом языке запросов (например, SQL), мог бы искать ConstantExpression с типом Expression вместо UnaryExpression со специальным типом узла Quote ивсе остальное было бы таким же.
Вы правы.Мы могли бы кодировать семантическую информацию, которая означает "вызывать семантику замыкания для этого значения" на , используя тип константного выражения в качестве флага .
«Константа» будет тогда иметь значение «использовать это постоянное значение», , если тип не является типом дерева выражений и значение является допустимым деревом выражения, в этом случае вместо этого используйте значение, которое является деревом выражений, полученным в результате переписывания внутренней части данного дерева выражений, чтобы вызвать семантику замыкания в контексте любых внешних лямбд, в которых мы могли бы быть прямо сейчас.
Нопочему будет делать эту сумасшедшую вещь? Оператор кавычек является безумно сложным оператором , и его следует использовать явно , если вы собираетесь его использовать.Вы предлагаете, чтобы не экономить на добавлении одного дополнительного метода фабрики и типа узла из нескольких десятков уже существующих, чтобы мы добавили странный угловой регистр к константам, чтобы константы иногда были логически константами, а иногда они переписывалисьлямбды с семантикой замыкания.
Это также будет иметь несколько странный эффект,Константа не означает «использовать это значение».Предположим, по какой-то причудливой причине, что вы хотели третий случай выше, чтобы скомпилировать дерево выражений в делегат, который раздает дерево выражений, которое имеет не переписанную ссылку на внешнюю переменную?Зачем?Возможно, потому что вы тестируете свой компилятор и хотите просто передать константу, чтобы потом можно было провести какой-то другой анализ.Ваше предложение сделает это невозможным;любая константа, имеющая тип дерева выражений, будет переписана независимо.Можно разумно ожидать, что «константа» означает «использовать это значение».«Константа» - это узел «делай, что я говорю».Работа постоянного процессора заключается не в том, чтобы угадать, что вы имели в виду , чтобы сказать, основываясь на типе.
И заметьте, конечно, что вы сейчас возлагаете бремя понимания (то есть понимания того, что константа имеет сложную семантику, что означает «константа» в одном случае, и «семантику индукции замыкания», основанную на флаге, который всистема типов ) для каждого поставщика, который выполняет семантический анализ дерева выражений, а не только для поставщиков Microsoft. Сколько из этих сторонних провайдеров ошиблись?
"Цитата" размахивает большим красным флажком с надписью "Эй, приятель, посмотри сюда, я - вложенная лямбда"выражение, и у меня есть дурацкая семантика, если я закрыт по внешней переменной! "в то время как «Константа» говорит: «Я не более чем ценность; используйте меня, как считаете нужным».Когда что-то сложно и опасно, мы хотим, чтобы оно показывало красные флаги, не скрывая этого факта, заставляя пользователя копаться в системе типов , чтобы выяснить, является ли это значение специальным или нет.
Более того, идея, что избежать избыточности - это даже цель, неверна.Конечно, избегать ненужного, сбивающего с толку избыточности - это цель, но большая часть избыточности - это хорошо;избыточность создает ясность.Новые фабричные методы и виды узлов: дешево .Мы можем сделать столько, сколько нам нужно, чтобы каждый из них четко представлял одну операцию.Нам не нужно прибегать к неприятным трюкам типа «это означает одно, если в этом поле не установлено это значение, в этом случае это означает что-то другое».