왜 진술 (j ++)인가? 금지?


169

다음 코드가 잘못되었습니다 ( ideone 참조 ).

public class Test
{
    public static void Main()
    {
        int j = 5;
        (j++);      // if we remove the "(" and ")" then this compiles fine.
    }
}

오류 CS0201 : 할당, 호출, 증분, 감소, 대기 및 새 객체 표현식 만 명령문으로 사용할 수 있습니다.

  1. 괄호를 제거 할 때 코드가 컴파일되는 이유는 무엇입니까?
  2. 왜 괄호로 컴파일되지 않습니까?
  3. C #은 왜 그렇게 설계 되었습니까?

29
@ C # 언어 디자인과 관련된 많은 사람들이 SO에 있기 때문에 내가 물었습니다. 이것에 문제가 있습니까?
user10607

34
"특정 프로그래밍 언어가 왜 이런 방식으로 이러한 특정 행동을 처리합니까?"보다 더 많은 주제에 대한 질문을 상상할 수 없습니다.
마법사 Xy

20
이제 Eric Lippert에 대한 답변이 있습니다. 수락 된 답변에 대한 결정을 다시 생각하고 싶을 수도 있습니다. 그것은 크리스마스이기 때문에 아마도 당신은 대답을 너무 일찍 받아 들였습니다.
Thomas Weller

17
@Servy 설계 결정이 문서화되지 않았으며 다른 사람에게 완전히 알려지지 않았다는 것을 의미합니까? 그는 것 없는 그것은 그가 추측에 대해 묻는 데요 의미하지 않는다 - 그는 대답을 요구하는 것, 추측 SO 사용자 요청. 사람들이 추측으로 대답 여부에 그들 에게이 없습니다. 영업 이익은 지적으로 그리고, 거기에 있는 스택 오버 플로우에 사람들이 C #에서 실제로 일을하고 이러한 결정을 만든.
Rob

5
@Rob : 문제는, 이론적 근거가 이미 어딘가에 문서화되어 있지 않다면, 추측은 재미 있고 누가 자신의 것을 공유하고 싶지 않습니까? 그러한 순수한 (또는 "교육받은") 추측을 확실하게 제거 할 수있는 방법을 찾으면 저에게 알려주십시오.
중복 제거기

답변:


221

깊은 통찰력에 감사드립니다.

최선을 다하겠습니다.

다른 답변에서 언급했듯이 컴파일러에서 표현식명령문 으로 사용되고 있음을 감지하고 있습니다. 많은 언어 (C, JavaScript 등)에서 표현식을 명령문으로 사용하는 것은 합법적입니다. 2 + 2;효력이없는 진술 임에도 불구하고 이러한 언어에서는 합법적입니다. 일부 표현식은 해당 값에만 유용하고 일부 표현식은 부작용 (예 : void returning method 호출)에만 유용하며 일부 표현식은 불행히도 두 가지 모두에 유용합니다. (증가처럼)

요점 : 표현만으로 구성되는 진술은 그 표현이 일반적으로 값보다 부작용에 더 유용한 것으로 생각되지 않는 한 거의 확실하게 오류 입니다. C # 디자이너는 일반적으로 부작용으로 간주되는 표현을 허용하고 일반적으로 가치에 유용한 것으로 간주되는 표현은 허용하지 않음으로써 중간 지점을 찾고자했습니다. C # 1.0에서 식별 한 식 집합은 증분, 감소, 메서드 호출, 할당 및 다소 논쟁의 여지가있는 생성자 호출이었습니다.


ASIDE : 일반적으로 대상의 구성은 구성의 부작용이 아닌 생성 된 값에 사용되는 것으로 생각합니다. 내 생각에 허용 new Foo();은 약간의 오해입니다. 특히, 실제 패턴에서 보안 결함을 일으키는이 패턴을 보았습니다.

catch(FooException ex) { new BarException(ex); } 

코드가 복잡하면이 결함을 발견하기가 놀랍게 어려울 수 있습니다.


따라서 컴파일러는 해당 목록에없는 표현식으로 구성된 모든 명령문을 감지합니다. 특히 괄호로 묶인 표현식은 괄호로 묶인 표현식으로 식별됩니다. "문 표현으로 허용됨"목록에 없으므로 허용되지 않습니다.

이 모든 것은 C # 언어의 디자인 원칙을 따르고 있습니다. 당신이 타이핑 (x++);했다면 당신은 아마 뭔가 잘못했을 것 입니다. 이것은 아마도 오타 M(x++);일 수도 있습니다. C # 컴파일러 팀 의 태도는 " 이 작업을 수행 할 수있는 방법을 찾아 낼 수 있는가? " 가 아니라는 점을 기억하십시오 . "C # 컴파일러 팀의 태도는" 유연한 코드가 실수 일 가능성이있는 경우 " 개발자에게 알려주십시오. "입니다. C # 개발자는 그런 태도를 좋아합니다.

이제, C # 사양이 실제로 몇 가지 이상한 경우 가 있습니다. 의미 또는 괄호는 허용하지만 C # 컴파일러 어쨌든을 허용하는 명백한 것을 상태. 대부분의 경우 지정된 동작과 허용 된 동작 사이의 사소한 불일치는 완전히 무해하므로 컴파일러 작성자는 이러한 작은 버그를 수정 한 적이 없습니다. 당신은 그에 대해 읽을 수 있습니다 :

수익률 myVar와 수익률 (myVar)간에 차이가 있습니까?


5
Re : "일반적으로 생성자의 호출은 구성의 부작용이 아닌 생성 된 값에 사용되는 것으로 생각합니다. 제 생각에는 이것이 약간의 오해입니다." 컴파일러가 그것을 금지하는 경우, 당신이 경우에 어떤 깨끗한 수정이 없을 것이라고했다 되는 부작용을 위해 호출. (대부분의 다른 금지 표현 문을 제외하고, 어떤 목적을 제공하지 않고 제거 할 수 있습니다 외부 부품이 ... ? ... : ...수정 프로그램을 사용하는 것입니다, if/ else대신합니다.)
ruakh

1
@ruakh : 이것은 권장하지 않는 행동이므로, 값이 싸고 간단한 수정이 있다면 깨끗하게 수정하지 않아도됩니다. 있는가; 변수에 할당하고 해당 변수를 사용하지 마십시오. 당신이 그것을 사용하고 있지 않다고 욕설을 원한다면, 그 코드 라인에 고유의 범위를 부여하십시오. 기술적으로 이것은 이것이 코드의 의미를 변경한다고 주장 할 수 있지만 (무용 한 참조 할당은 여전히 ​​쓸모없는 참조 할당입니다) 실제로는 문제가되지 않습니다. 나는 약간 다른 IL을 생성한다는 것을 인정하지만 최적화없이 컴파일 된 경우에만 가능합니다.
Brian

입력 할 때 Safari, Firefox 및 Chrome에서 모두 ReferenceError가 발생 contineu;합니다.
Brian McCutchon

2
@McBrainy : 네 말이 맞아. 맞춤법 오류로 인한 결함을 잘못 기억하고 있습니다. 메모를 확인하겠습니다. 그 동안 여기에 더 큰 지점과 관련이 없기 때문에 문제가되는 진술을 삭제했습니다.
Eric Lippert

1
@ 마이크 : 동의합니다. 우리가 때때로 진술을 보는 또 다른 상황은 테스트 사례와 try { new Foo(null); } catch (ArgumentNullException)...같지만 분명히 이러한 상황은 정의상 생산 코드가 아닙니다. 더미 변수에 할당하기 위해 그러한 코드를 작성할 수있는 것이 합리적입니다.
Eric Lippert

46

에서 C # 언어 사양

식 문은 식을 평가하는 데 사용됩니다. 명령문으로 사용할 수있는 표현식에는 메소드 호출, 새 연산자를 사용한 오브젝트 할당, =를 사용한 지정 및 복합 지정 연산자, ++ 및-연산자를 사용한 증분 및 감소 조작 및 식 대기가 포함됩니다.

명령문을 괄호로 묶으면 소위 괄호로 묶인 표현식이 새로 작성됩니다. 사양에서 :

괄호로 묶인 표현식은 괄호로 묶인 표현식으로 구성됩니다. ... 괄호 안의 표현식을 평가하여 괄호 안의 표현식을 평가합니다. 괄호 안의 표현식이 네임 스페이스 또는 유형을 나타내는 경우 컴파일 타임 오류가 발생합니다. 그렇지 않으면 괄호로 묶은 표현식의 결과는 포함 된 표현식의 평가 결과입니다.

괄호로 묶은 표현식은 유효한 표현식 명령문으로 나열되지 않으므로 스펙에 따라 올바른 명령문이 아닙니다. 디자이너가 그것을 할 선택한 이유이 방법은 누구의 추측이지만 전체 문을 괄호에 포함되는 경우 괄호 유용한 일을하지 않기 때문에 내 건은 다음과 같습니다 stmt(stmt)정확히 동일합니다.


4
@Servy : 자세히 읽으면 그보다 약간 미묘한 차이가 있습니다. 소위 "Expression Statement"는 실제로 유효한 명령문이며, 스펙은 유효한 명령문 일 수있는 표현식 유형을 나열합니다. 그러나 스펙에서 "괄호로 묶은 표현식"이라는 표현식 유형이 유효한 표현식 명령문으로 사용할 수있는 유효한 표현식 목록에없는 것으로 반복합니다.
Frank Bryce

4
OP언어 디자인에 대해 아무것도 요구하지 않습니다. 그는 왜 이것이 오류인지 알고 싶어합니다. 대답은 올바른 진술이 아니기 때문 입니다.
Leandro

9
알았어. 답은 , 때문에 사양에 따라 , 유효한 문이 아니다. 거기 있습니다 : 디자인 이유!
Leandro

3
문제는 언어 가이 구문을 처리하도록 설계된 이유에 대해 심도 있고 디자인 중심적인 이유를 묻지 않습니다. 그는 다른 코드 조각 (초보자에게 행동 해야하는 것처럼 보일 때 하나의 코드가 컴파일되는 이유를 묻습니다) 동일하게) 컴파일에 실패합니다. 이 게시물은 그에 대한 답변입니다.
마법사 Xy

4
@Servy JohnCarpenter는 "그렇게 할 수 없다"고 말하는 것이 아닙니다. 그는 당신이 혼란스러워했던 두 가지가 동일하지 않다고 말합니다. j ++는 Expression Statement이고 (j ++)는 괄호로 묶은 표현식입니다. 갑자기 OP는 이제 차이점과 그 차이점을 알고 있습니다. 좋은 대답입니다. 제목이 아닌 그의 질문에 답한다. 질문 제목에 "design"이라는 단어가 많은 논쟁을 불러 일으킨다 고 생각하지만, 이는 사양에서 수집 한 디자이너가 대답 할 필요는 없습니다.
thinklarge

19

주위에 괄호를 beacause i++ 는 표현식을 작성 / 정의하고 있습니다. 오류 메시지에서 알 수 있듯이 간단한 표현식은 명령문으로 사용할 수 없습니다.

왜 언어가 이런 식으로 설계 되었습니까? 코드처럼 오해를 일으키지 않는 문장으로 오해의 소지가있는 버그를 방지하기 위해

int j = 5;
j+1; 

두 번째 줄은 효과가 없습니다 (그러나 눈치 채지 못할 수도 있습니다). 그러나 컴파일러 대신 코드를 제거하는 대신 (코드가 필요하지 않기 때문에) 명시 적으로 코드를 제거하도록 요청하거나 무언가를 입력하지 않은 경우 수정해야합니다.

편집 :

c #의 대괄호 (캐스트 및 함수 호출과 같은 다른 용도 외에)는 표현식을 그룹화하고 단일 표현식 (하위 표현식 작성)을 리턴하는 데 사용됩니다.

그 코드 수준에서는 오직 stament 만 허용됩니다.

j++; is a valid statement because it produces side effects

근데 용감한 표현을 사용하면 표현으로 변합니다

myTempExpression = (j++)

myTempExpression;

컴파일러가 표현식을 부작용으로 확신 할 수 없기 때문에 유효하지 않습니다 (중지 문제가 발생하지 않는 한).


네, 저도 처음에 생각한 것입니다. 그러나 이것은 부작용 있습니다. j변수 증가 후 사후 증가를 수행 합니까?
user10607

1
j++;유효한 진술이지만 대괄호를 사용하여 let me take this stement and turn it into an expression.. 라고 말하고 코드에서 해당 시점에 설명이 유효하지 않습니다.
CaldasGSM

"compute and ignore"문이없는 것은 너무 나쁩니다. 코드에서 예외를 발생시키지 않고 값을 계산할 수는 있지만 실제 계산 된 값은 신경 쓰지 않을 수 있기 때문입니다. 계산 및 무시 명령문은 자주 사용되지 않지만 그러한 경우 프로그래머의 의도를 명확하게합니다.
supercat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.