부모 범위의 변수와 이름이 같은 자식 변수를 선언 할 수있는 이유는 무엇입니까?


23

나는 최근에 이미 같은 이름의 변수가있는 함수 내에 선언 된 작업의 매개 변수로 변수 이름을 실수로 재사용하는 일부 코드를 작성했습니다. 예를 들면 다음과 같습니다.

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

중복을 발견했을 때 코드가 컴파일되고 완벽하게 실행되었다는 사실에 놀랐습니다. 이것은 C #의 범위에 대해 알고있는 것을 기반으로 기대되는 동작이 아닙니다. 일부 빠른 인터넷 검색은 유사한 코드 로 인해 Lambda Scope Clarification 과 같은 오류 발생 한다고 불평하는 SO 질문을 제기했습니다 . (저는 샘플 코드를 IDE에 붙여 넣어 실행 여부를 확인하고 완벽하게 실행합니다.) 또한 Visual Studio에서 이름 바꾸기 대화 상자를 시작하면 첫 번째 이름이 이름 충돌로 강조 표시됩니다.x

이 코드는 왜 작동합니까? Visual Studio 2019에서 C # 8을 사용하고 있습니다.


1
람다는 컴파일러가 생성 한 클래스의 메소드로 이동하므로 x해당 메소드 의 전체 매개 변수가 범위를 벗어납니다. 예를 들어 sharplab 을 참조하십시오 .
Lasse V. Karlsen

6
여기서 C # 7.3을 대상으로 할 때 컴파일되지 않으므로 C # 8에만 적용되는 것으로 보입니다.
Jonathon Chase

링크 된 질문의 코드는 sharplab 에서 잘 컴파일됩니다 . 최근 변경되었을 수 있습니다.
Lasse V. Karlsen

2
답이없는 속임수를 찾았습니다 : stackoverflow.com/questions/58639477/…
bolov

답변:


26

이 코드는 왜 작동합니까? Visual Studio 2019에서 C # 8을 사용하고 있습니다.

당신은 당신의 자신의 질문에 대답했습니다! C # 8을 사용하고 있기 때문입니다.

C # 1에서 7까지의 규칙은 다음과 같습니다. 간단한 이름을 사용하여 동일한 로컬 범위에서 서로 다른 두 가지를 의미 할 수 없습니다. (실제 규칙은 그것보다 약간 더 복잡하지만 지루한 방법을 설명합니다. 자세한 내용은 C # 사양을 참조하십시오.)

이 규칙의 목적은 당신의 예에서 이야기하고있는 상황을 막는 것이 었습니다. 특히이 규칙은 다음과 같은 혼동을 방지하기 위해 고안되었습니다.

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

그리고 지금 우리의 몸 내부의 상황이 곳이 M, x수단 모두 this.x와 지역을 x.

의도가 좋았지 만이 규칙에는 여러 가지 문제가있었습니다.

  • 사양에 맞게 구현되지 않았습니다. 간단한 이름을 유형과 속성으로 사용할 수있는 상황이 있었지만 오류 감지 논리에 결함이 있기 때문에 항상 오류로 표시되지 않았습니다. (아래 참조)
  • 오류 메시지가 혼란스럽게 표시되고 일관되지 않게보고되었습니다. 이 상황에 대해 여러 가지 다른 오류 메시지가있었습니다. 그들은 일관되게 범죄자를 식별했다. 즉, 때로는 내부 사용이 호출되고 때로는 외부 가 호출 되고 때로는 혼란 스러웠습니다.

나는 Roslyn을 다시 써서 이것을 정리하려고 노력했다. 새 오류 메시지를 추가하고 오류가보고 된 위치와 관련하여 이전 오류 메시지를 일관되게 만들었습니다. 그러나이 노력은 너무 적었고 너무 늦었다.

C # 팀은 C # 8에 대해 전체 규칙이 방지하는 것보다 더 많은 혼란을 야기하고 규칙에서 언어가 폐기되었다고 결정했습니다. (퇴직시기를 결정한 Jonathon Chase에게 감사드립니다.)

이 문제의 이력과이 문제를 해결하려는 방법에 관심이있는 경우, 내가 작성한이 기사를 참조하십시오.

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/

3 부 끝에서이 기능과 "색상"기능, 즉 다음을 허용하는 기능간에 상호 작용이 있음을 언급했습니다.

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

여기서 우리는 간단한 이름 Color을 사용 this.Color하여 열거 된 형식과 둘 다를 참조했습니다 Color. 사양을 엄격히 읽으면 오류가 발생하지만이 경우 사양이 잘못되어 의도가 허용됩니다.이 코드는 명확하지 않으며 개발자가 변경하게하기 때문에 까다로울 것입니다.

나는이 두 규칙 사이의 모든 이상한 상호 작용을 설명하는 기사를 쓰지 않았으며 지금 그렇게하는 것은 무의미합니다!


문제의 코드가 C # 6, 7, 7.1, 7.2 및 7.3에 대해 컴파일되지 않아 "CS0136 : 이름이 'x'인 로컬 또는 매개 변수를이 범위에서 선언 할 수 없습니다." 규칙은 여전히 ​​C # 8까지 시행 된 것처럼 보입니다.
Jonathon Chase

@ JonathonChase : 감사합니다!
Eric Lippert
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.