Почему я могу объявить дочернюю переменную с тем же именем, что и переменная в родительской области? - PullRequest
23 голосов
/ 29 января 2020

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

var x = 1;
Action<int> myAction = (x) => { Console.WriteLine(x); };

Когда я обнаружил дублирование, я был удивлен, увидев, что код скомпилирован и работает отлично, что я не ожидаю, основываясь на том, что я знаю о области действия в C#. Некоторые быстрые поиски в Google подняли SO вопросы, которые жалуются, что подобный код действительно вызывает ошибку, такую ​​как Lambda Scope Clarification . (Я вставил этот пример кода в свою среду IDE, чтобы посмотреть, будет ли он работать, просто чтобы убедиться, что он работает отлично.) Кроме того, когда я вхожу в диалоговое окно «Переименование» в Visual Studio, первый x выделяется как конфликт имен.

Почему этот код работает? Я использую C# 8 с Visual Studio 2019.

1 Ответ

26 голосов
/ 29 января 2020

Почему этот код работает? Я использую C# 8 с Visual Studio 2019.

Вы ответили на свой вопрос! Это потому, что вы используете C# 8.

Правило от C# 1 до 7 гласило: простое имя нельзя использовать для обозначения двух разных вещей в одной локальной области. (Действительное правило было несколько сложнее, но описывало, как это утомительно; подробности см. В спецификации C#.)

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

class C 
{
  int x;
  void M()
  {
    x = 123;
    if (whatever)
    {
      int x = 356;
      ...

И теперь у нас есть ситуация, когда внутри тела M, x означает как this.x, так и локальное x .

Несмотря на благие намерения, с этим правилом возникло несколько проблем:

  • Оно не было реализовано до c. Были ситуации, когда простое имя могло использоваться как, скажем, как тип, так и свойство, но они не всегда были помечены как ошибки, поскольку логика обнаружения ошибок c была ошибочной. (См. Ниже)
  • Сообщения об ошибках были сбиты с толку и содержали непоследовательные сообщения. Для этой ситуации было несколько разных сообщений об ошибках. Они непоследовательно опознали преступника; то есть иногда вызывается использование inner , иногда external , а иногда это просто сбивает с толку.

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

Команда C# решила для C# 8, что все правило вызывает больше путаницы, чем предотвращает, и правило было удалено из языка. (Спасибо Джонатону Чейзу за то, что он определил, когда произошел выход на пенсию.)

Если вам интересно узнать историю этой проблемы и то, как я пытался ее исправить, см. Следующие статьи, которые я написал об этом:

https://ericlippert.com/2009/11/02/simple-names-are-not-so-simple/

https://ericlippert.com/2009/11/05/simple-names-are-not-so-simple-part-two/

https://ericlippert.com/2014/09/25/confusing-errors-for-a-confusing-feature-part-one/

https://ericlippert.com/2014/09/29/confusing-errors-for-a-confusing-feature-part-two/

https://ericlippert.com/2014/10/03/confusing-errors-for-a-confusing-feature-part-three/

В конце третьей части я заметил, что между этой функцией и функцией «Цветовой цвет» также было взаимодействие - то есть функция, которая позволяет:

class C
{
  Color Color { get; set; }
  void M()
  {
    Color = Color.Red;
  }
}

Здесь мы использовали простое имя Color для обозначения как this.Color, так и перечислимого типа Color; в соответствии со строгим прочтением спецификации, это должно быть ошибкой, но в этом случае spe c был неправильным, и намерение было разрешить это, так как этот код однозначен, и было бы неприятно заставить разработчика изменить его.

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

...