Может ли использование yield в качестве контекстного ключевого слова вызвать проблемы? - PullRequest
0 голосов
/ 30 сентября 2018

В Essential C # указано:

После C # 1.0 новые зарезервированные ключевые слова не были введены в C #.Однако некоторые конструкции в более поздних версиях используют контекстные ключевые слова, которые имеют значение только в определенных местах.Вне этих обозначенных местоположений контекстные ключевые слова не имеют особого значения. * При использовании этого метода большая часть кода C # 1.0 совместима с более поздними стандартами.

* Например, в начале разработки C # 2.0 разработчики языка определялиyield в качестве ключевого слова, и Microsoft выпустила альфа-версии компилятора C # 2.0 с помощью yield в качестве назначенного ключевого слова для тысяч разработчиков.Однако разработчики языка в конечном итоге определили, что, используя yield return, а не yield, в конечном итоге они могли бы избежать добавления yield в качестве ключевого слова, поскольку оно не имело бы особого значения вне его близости к возвращению.

Теперь я не наденуНе понимаю этого, как и до c # 2.0, у каждого метода, возвращающего IEnumerable, должен был бы быть оператор return, тогда как yield мог бы использоваться в качестве контекстного ключевого слова только внутри метода, который возвратил IEnumerable, но не имел оператора return.Например,

public IEnumerable<int> GetInts()
{
    for (int i = 0; i < 1000; i++)
        yield i;
}

Поскольку этот метод не скомпилировал бы pre-C # 2.0, я не понимаю, как это может нарушить обратную совместимость.

Поэтому мой вопрос:

Существуют ли ситуации, когда использование yield вместо yield return в C # нарушило бы обратную совместимость или иным образом вызывало проблемы?

Ответы [ 2 ]

0 голосов
/ 30 сентября 2018

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

например.

public class yield
{

}
...
public IEnumerable<yield> GetInts()
{
    yield item;
    item = new yield(); //compiler complains - item not declared.

    yield item2 = new yield(); //compiler complains-item2 not declared.
}

или

public int yield(int x) => 5;
public IEnumerable<int> GetInts()
{
    yield (5); //does not call the yield method as expected, but yields 5 instead
}

Так что, хотя ни один из этих методов не скомпилировал бы до C # 2.0 в любом случае,с помощью возврата доходности уклонился от всех этих проблем.

0 голосов
/ 30 сентября 2018

Проблема

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 позволяет избежать двусмысленности без каких-либо несоответствий и проста в реализации.

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