나는이 코드를 작성했다 :
private static Expression<Func<Binding, bool>> ToExpression(BindingCriterion criterion)
{
switch (criterion.ChangeAction)
{
case BindingType.Inherited:
var action = (byte)ChangeAction.Inherit;
return (x => x.Action == action);
case BindingType.ExplicitValue:
var action = (byte)ChangeAction.SetValue;
return (x => x.Action == action);
default:
// TODO: Localize errors
throw new InvalidOperationException("Invalid criterion.");
}
}
그리고 컴파일 오류를 발견 한 것에 놀랐습니다.
‘action’이라는 지역 변수가 이미이 범위에 정의되어 있습니다.
해결하기 매우 쉬운 문제였습니다. 두 번째를 제거하는 var
것이 트릭을 수행했습니다.
분명히 case
블록에 선언 된 변수는 parent의 범위를 switch
갖지만 이것이 왜 그런지 궁금합니다. C #은 실행이 (가 다른 경우를 통해 떨어지지 않는 것을 감안할 필요 break
, return
, throw
, 또는 goto case
모든의 끝에 문 case
블록), 그것은 하나의 내부는 변수 선언을 허용 것이 매우 이상한 것 같다 case
다른 변수와 함께 사용하거나 충돌 할 case
. 다시 말해 case
, 실행이 불가능하더라도 변수는 명령문을 통과하는 것으로 보입니다 . C #은 혼란 스럽거나 쉽게 남용되는 다른 언어의 일부 구문을 금지하여 가독성을 높이기 위해 많은 노력을 기울입니다. 그러나 이것은 혼란을 야기 할 수밖에없는 것처럼 보입니다. 다음 시나리오를 고려하십시오.
-
이것을 다음과 같이 바꾸려면 :
case BindingType.Inherited: var action = (byte)ChangeAction.Inherit; return (x => x.Action == action); case BindingType.ExplicitValue: return (x => x.Action == action);
” 할당되지 않은 로컬 변수 ‘action’ “이 사용되었습니다. 내가 생각할 수있는 C #의 다른 모든 구문에서
var action = ...
변수를 초기화 하기 때문에 혼란 스럽지만 여기서는 단순히 변수를 선언합니다. -
내가 이런 경우를 바꾸려면 :
case BindingType.ExplicitValue: action = (byte)ChangeAction.SetValue; return (x => x.Action == action); case BindingType.Inherited: var action = (byte)ChangeAction.Inherit; return (x => x.Action == action);
” 로컬 변수 ‘action’을 (를) 선언하기 전에 사용할 수 없습니다 “가 표시됩니다. 일반적으로 내가 어떤 주문 I 소원 이러한 쓸 수 있지만,이 때문에 – 케이스 블록의 순서는 완전히 명확하지의 방법으로 여기에 중요한 것으로 나타납니다 그래서
var
첫 번째 블록에 표시되어야합니다action
사용되는, 내가 조정할 필요가case
블록을 따라서. -
이것을 다음과 같이 바꾸려면 :
case BindingType.Inherited: var action = (byte)ChangeAction.Inherit; return (x => x.Action == action); case BindingType.ExplicitValue: action = (byte)ChangeAction.SetValue; goto case BindingType.Inherited;
그런 다음 오류가 발생하지 않지만 어떤 의미에서는 변수가 선언되기 전에 값이 할당 된 것처럼 보입니다 .
(실제로 이것을하고 싶을 때를 생각할 수는 없지만goto case
오늘 전에는 존재 조차 알지 못했습니다 )
그래서 제 질문은 C #의 디자이너가 왜 case
자신의 로컬 범위를 블록에 제공하지 않았 습니까? 이에 대한 역사적 또는 기술적 이유가 있습니까?
답변
다른 모든 경우에“정상”로컬 변수의 범위는 중괄호 ( )로 구분 된 블록 이기 때문에 좋은 이유라고 생각합니다 {}
. 일반적이지 않은 지역 변수는 for
루프 변수 나로 선언 된 변수 와 같이 명령문 앞에있는 특수 구문 (보통 블록)에 나타납니다 using
.
또 다른 예외는 LINQ 쿼리 표현식의 로컬 변수이지만 일반적인 로컬 변수 선언과는 완전히 다르므로 혼동 될 가능성이 없다고 생각합니다.
참고로 규칙은 C # 사양의 §3.7 범위에 있습니다.
local-variable-declaration에 선언 된 로컬 변수의 범위는 선언 이 발생하는 블록입니다.
명령문의 스위치 블록 에 선언 된 로컬 변수의 범위
switch
는 switch-block 입니다.명령문 의 for-initializer 에 선언 된 로컬 변수의 범위는 for-initializer , for-condition , for-iterator 및 명령문 의 포함 된 명령문 입니다 .
for
for
foreach-statement , using-statement , lock-statement 또는 query-expression의 일부로 선언 된 변수의 범위 는 주어진 구문의 확장에 의해 결정됩니다.
(나는 switch
언급 된 다른 모든 구문과 달리 로컬 변수 감소에 대한 특별한 구문이 없으므로 블록이 명시 적으로 언급 된 이유를 완전히 확신 하지는 못합니다.)
답변
그러나 그렇습니다. 라인을 줄 바꿈하여 어디에서나 로컬 범위를 만들 수 있습니다{}
switch (criterion.ChangeAction)
{
case BindingType.Inherited:
{
var action = (byte)ChangeAction.Inherit;
return (x => x.Action == action);
}
case BindingType.ExplicitValue:
{
var action = (byte)ChangeAction.SetValue;
return (x => x.Action == action);
}
default:
// TODO: Localize errors
throw new InvalidOperationException("Invalid criterion.");
}
답변
에릭 리퍼 (Eric Lippert)를 인용하겠습니다.
합리적인 질문은 “왜 이것이 합법적이지 않습니까?”입니다. 합리적인 대답은 “음, 왜 그래야합니까?”입니다. 두 가지 방법 중 하나를 사용할 수 있습니다. 이것은 합법적입니다.
switch(y) { case 1: int x = 123; ... break; case 2: int x = 456; ... break; }
또는 이것은 합법적입니다.
switch(y) { case 1: int x = 123; ... break; case 2: x = 456; ... break; }
그러나 당신은 두 가지 방법으로 가질 수 없습니다. C #의 디자이너는 두 번째 방법을보다 자연스러운 방법으로 선택했습니다.
이 결정은 1999 년 7 월 7 일에 이루어졌으며 10 년 전에 부끄러웠다. 그 날의 노트에있는 주석은 아주 간단합니다. “스위치 케이스는 자체 선언 공간을 만들지 않습니다”라고 말한 다음 무엇이 작동하고 무엇이 작동하지 않는지를 보여주는 샘플 코드를 제공합니다.
이 특별한 날에 디자이너들의 생각에 대해 더 많은 것을 알기 위해서는 10 년 전에 생각했던 것에 대해 많은 사람들을 괴롭 히고 궁극적으로 사소한 문제에 대해 버그를 제기해야합니다. 나는 그렇게하지 않을 것입니다.
요컨대, 한 가지 방법을 선택해야 할 특별한 이유는 없습니다. 둘 다 장점이 있습니다. 언어 디자인 팀은 하나를 선택해야했기 때문에 한 가지 방법을 선택했습니다. 그들이 선택한 것은 나에게 합리적인 것 같습니다.
따라서 Eric Lippert보다 1999 년 C # 개발자 팀에 더 많은 시간을 보내지 않으면 정확한 이유를 알 수 없습니다!
답변
설명은 간단합니다. C와 비슷하기 때문입니다. C ++, Java 및 C #과 같은 언어는 친숙 함을 위해 switch 문의 구문과 범위를 복사했습니다.
(다른 답변에서 언급했듯이 C # 개발자는 사례 범위와 관련하여 이러한 결정을 내리는 방법에 대한 문서를 가지고 있지 않지만 C # 구문에 대한 명시되지 않은 원칙은 다르게 할 이유가 없다면 Java를 복사한다는 것입니다. )
C에서 사례 진술은 goto-labels와 유사합니다. switch 문은 실제로 계산 된 goto에 대한 더 좋은 구문입니다 . 케이스 는 스위치 블록 의 진입 점 을 정의합니다 . 명시적인 종료가 없으면 기본적으로 나머지 코드가 실행됩니다. 따라서 동일한 범위를 사용하는 것이 좋습니다.
(보다 근본적으로 Goto는 구조화되어 있지 않습니다. 코드 섹션 을 정의하거나 구분하지 않고 점프 포인트 만 정의하므로 goto 레이블 은 범위를 도입 할 수 없습니다 .)
C #은 구문을 유지하지만 비어 있지 않은 각 경우 절 뒤에 종료를 요구하여 “fall through”에 대한 보호 조치를 도입합니다. 그러나 이것은 스위치에 대한 우리의 생각을 변화시킵니다! 케이스는 이제 if-else의 분기와 같은 대체 분기 입니다. 이는 각 분기가 if-clauses 또는 iteration 절과 같이 자체 범위를 정의 할 것으로 예상한다는 의미입니다.
간단히 말해 : 사례는 C와 같은 방식이므로 동일한 범위를 공유합니다. 그러나 사례를 goto 대상이 아닌 대체 브랜치로 생각하기 때문에 C #에서는 이상하고 일치하지 않습니다.
답변
범위를 보는 간단한 방법은 범위를 블록별로 고려하는 것 {}
입니다.
switch
블록을 포함하지 않기 때문에 다른 범위를 가질 수 없습니다.