Проблема
for (int i = 0; i < 1000; i++)
yield i;
Это действительно недопустимо без ключевого слова yield, но что если мы добавим круглые скобки вокруг i
?
for (int i = 0; i < 1000; i++)
yield (i);
Теперь это вполне допустимый вызовметода с именем yield
.Поэтому, если мы интерпретируем yield (i);
как использование контекстного ключевого слова yield
, смысл этого действительного кода изменится, что нарушит обратную совместимость.
Более формальный взгляд на это будет выглядеть так:Если мы изменим грамматику C # 2, чтобы заменить statement: 'yield' 'return' expression ';'
на statement: 'yield' expression ';'
, то между этим правилом и правилом для вызовов функций возникнет неоднозначность, поскольку expression
может быть выведено из '(' expression ')'
, а 'yield' '(' expression ')' ';'
можеттакже может быть вызовом функции в выражении выражения.
Возможное решение 1
Можно, конечно, сказать, что только yield i;
(или любое другое выражение, которое не начинается с открывающей скобки) должноинтерпретироваться как использование контекстного ключевого слова, тогда как yield (i);
будет по-прежнему рассматриваться как вызов метода.Однако это было бы довольно противоречивым и удивительным поведением - добавление скобок вокруг выражения не должно изменять семантику подобным образом.
Также это будет означать изменение вышеуказанного правила грамматики на что-то вроде statement: 'yield' expressionNoStartingParen ';'
и затем определение expressionNoStartingParen
, что дублирует большую часть фактического определения expression
.Это сделало бы грамматику довольно сложной (хотя вы могли бы обойти это, просто описав требование «без начальных скобок» в словах, а не в грамматике, а затем использовать флаг для отслеживания этого в реальных реализациях (хотя это, вероятно, не будетвариант, использующий большинство генераторов синтаксического анализатора)).
Возможное решение 2
Еще один способ устранить эту неоднозначность, о которой вы упоминали в комментариях, - это интерпретировать только yield expression;
как результатоператор, находящийся внутри не пустых методов, у которых нет оператора возврата.Это будет поддерживать обратную совместимость, потому что такие методы будут в любом случае недопустимы в C # 1.Однако это было бы несколько противоречиво, потому что теперь вы можете определить метод с именем yield и вызывать его в методах, которые не используют операторы yield, но не в методах, которые это делают.
Более важно, что это не то, что контекстное ключевое словокак правило, как.Обычно контекстное ключевое слово действует как идентификатор всякий раз, когда оно используется в любом месте, где идентификаторы действительны, и может использоваться в качестве ключевого слова только в тех местах, где идентификаторы не могут встречаться.Это не будет иметь место здесь.Это не только не согласуется с тем, как обычно работают контекстные ключевые слова, и это затруднит для читателей различие между «доходом как ключевым словом» и выходом как идентификатором, но и усложнит реализацию:
Мало того, что вы не сможете определить, является ли yield(x);
оператором доходности, просто взглянув на эту строку (вам нужно взглянуть на весь метод);парсер тоже не будет - ему нужно знать, содержит ли метод оператор return
.Это потребует двух разных определений для тел с грамматикой и без возврата в грамматике - и отдельного определения того, что разрешено в качестве идентификатора в каждом.Это была бы ужасная грамматика, которую нужно было бы рассмотреть и реализовать.
На практике, скорее всего, можно создать неоднозначную грамматику, а затем проанализировать yield (x);
в заполнитель AST, который содержит как возможность того, что это выражение yieldили вызов функции.Затем вы попытаетесь проверить оба типа и выбросить тот, который не проверяет тип.Это бы сработало, но это довольно редко и потребовало бы значительных изменений в том, как синтаксический анализ работает в компиляторе и как он затем работает с AST.Любые другие реализации языка (Mono, Roslyn) также должны были бы иметь дело с этой сложностью, затрудняя создание новых реализаций.
Заключение
Итак, в заключение, оба способа обойти эту проблему приводят к некоторым несоответствиям, и последний также значительно труден для реализации.Только трактовка yield
как особой при использовании вместе с return
позволяет избежать двусмысленности без каких-либо несоответствий и проста в реализации.