C #에서 const 매개 변수가 허용되지 않는 이유는 무엇입니까?


102

특히 C ++ 개발자에게는 이상하게 보입니다. C ++에서는 const메서드에서 상태가 변경되지 않도록 매개 변수를 다음과 같이 표시했습니다 . const refref로 전달하기 위해 전달하고 상태가 변경되지 않는지 확인하는 것과 같은 다른 C ++ 특정 이유도 있습니다. 그런데 왜 C #에서 메서드 매개 변수로 const로 표시 할 수 없습니까?

다음과 같이 메서드를 선언 할 수없는 이유는 무엇입니까?

    ....
    static void TestMethod1(const MyClass val)
    {}
    ....
    static void TestMethod2(const int val)
    {}
    ....

그것은 항상 C ++의 안전한 기능이었고 내가 본 것처럼 언어에 진정으로 통합되지 않았습니다. C # 개발자는 그것이 분명히 많은 가치를 추가한다고 생각하지 않았습니다.
Noldorin

3
이 질문에 더 큰 토론 : "C #에서 헌장-정확성" stackoverflow.com/questions/114149/const-correctness-in-c
tenpn

값 유형 [out / ref]에 대한 것이 있지만 참조 유형은 없습니다. 흥미로운 질문입니다.
kenny

4
어떻게이 질문에 C # 및 언어에 구애받지 않는 태그가 모두있을 수 있습니까?
CJ7

1
불변 데이터와 부작용이없는 함수는 추론하기가 더 쉽습니다. 당신은 F 번호를 즐길 수 fsharpforfunandprofit.com/posts/is-your-language-unreasonable
대령 패닉

답변:


59

다른 좋은 답변 외에도 C 스타일의 constness를 C #에 넣지 않는 또 다른 이유를 추가하겠습니다. 당신은 말했다 :

메서드에서 상태가 변경되지 않도록 매개 변수를 const로 표시합니다.

const가 실제로 그렇게했다면 그것은 좋을 것입니다. Const는 그렇게하지 않습니다. const는 거짓말입니다!

Const는 실제로 사용할 수 있다는 보장을 제공하지 않습니다. const 일을 취하는 메소드가 있다고 가정하십시오. 두 명의 코드 작성자가 있습니다. 호출자를 작성하는 사람수신자를 작성하는 사람 입니다. 피 호출자의 작성자는 메소드가 const를 취하도록 만들었습니다. 두 저자는 객체에 대해 무엇이 변하지 않는다고 가정 할 수 있습니까?

아무것도. 피 호출자는 const를 캐스트하고 객체를 변경할 수 있으므로 호출자는 const를 취하는 메서드를 호출하는 것이 실제로 변경되지 않는다는 보장 이 없습니다. 마찬가지로 피 호출자는 객체의 내용이 피 호출자의 작업 내내 변경되지 않을 것이라고 가정 할 수 없습니다. 피 호출자 는 const 객체의 비 const 별칭 에 대해 일부 mutating 메서드를 호출 할 수 있으며 이제 소위 const 객체가 변경되었습니다 .

C 스타일 const는 객체가 변경되지 않아 손상된다는 보장을 제공하지 않습니다. 이제 C는 이미 원할 경우 double을 int로 재 해석 할 수있는 약한 유형 시스템을 가지고 있으므로 const에 대해서도 약한 유형 시스템을 가지고 있다는 것은 놀라운 일이 아닙니다. 그러나 C #은 좋은 형식 시스템 을 갖도록 설계되었습니다. "이 변수에는 문자열이 포함되어 있습니다"라고 말하면 변수가 실제로 문자열 (또는 null)에 대한 참조를 포함하는 형식 시스템 입니다. 우리 는 타입 시스템이 거짓말이되는 것을 원하지 않기 때문에 C 스타일의 "const"수정자를 타입 시스템에 넣는 것을 절대 원하지 않습니다 . 코드에 대해 올바르게 추론 할 수 있도록 유형 시스템이 강력 하기를 바랍니다 .

Const in C는 지침입니다 . 그것은 기본적으로 "이것을 변형 시키려하지 않기 위해 나를 믿을 수있다"는 의미입니다. 그것은 타입 시스템 에 있어서는 안됩니다 . 에서 물건 타입 시스템은 해야 사실 당신이하지에 대해 추론 할 수있는 개체에 대한 가이드 라인 의 사용합니다.

자, 오해하지 마십시오. C의 const가 깊이 깨 졌다고해서 전체 개념이 쓸모 없다는 의미는 아닙니다. 내가보고 싶은 것은 C #에서 실제로 정확 하고 유용한 형태의 "const"주석입니다. 이는 인간과 컴파일러 모두가 코드를 이해하는 데 사용할 수 있고 런타임이 자동 병렬화와 같은 작업을 수행하는 데 사용할 수있는 주석입니다. 기타 고급 최적화.

예를 들어, 한 덩어리의 코드 주위에 "상자를 그리고" 컴파일러가 확인할 수있는 방식으로 "이 코드 덩어리가이 클래스의 어떤 필드에도 변형을 수행하지 않는다는 것을 보장 합니다 "라고 말할 수 있다고 상상해보십시오 . 또는 "이 순수한 방법은 개체의 내부 상태를 변경하지만 상자 외부에서 관찰 할 수있는 방식은 변경하지 않습니다."라는 상자를 그립니다. 이러한 객체는 자동으로 안전하게 다중 스레드 될 수 없지만 자동으로 메모 될 수 있습니다 . 풍부한 최적화와 더 깊은 이해를 가능하게하는 코드에 추가 할 수있는 모든 종류의 흥미로운 주석이 있습니다. 약한 C 스타일의 const 주석보다 더 잘할 수 있습니다.

그러나 나는 이것이 단지 추측이라고 강조한다 . 우리는 이런 종류의 기능을 C #의 가상 미래 버전에 넣을 확고한 계획이 없습니다. 이것은 제가보고 싶은 것입니다. 다가오는 멀티 코어 컴퓨팅에 대한 강조가 필요할 수도 있지만, 어떤 식 으로든 C #의 특정 기능 또는 미래 방향에 대한 예측이나 보장으로 해석되어서는 안됩니다.

이제 원하는 것이 "이 매개 변수의 값은 메소드 전체에서 변경되지 않습니다"라는 매개 변수 인 로컬 변수에 대한 주석뿐이라면, 확실히 쉽게 수행 할 수 있습니다. 한 번 초기화되는 "읽기 전용"지역 및 매개 변수와 메서드에서 변경되는 컴파일 타임 오류를 지원할 수 있습니다. "using"문에 의해 선언 된 변수는 이미 그러한 지역입니다. 모든 지역 및 매개 변수에 선택적 주석을 추가하여 "사용"변수처럼 작동하도록 만들 수 있습니다. 우선 순위가 매우 높은 기능이 아니므로 구현 된 적이 없습니다.


34
미안하지만이 주장은 매우 약하다는 것을 알았습니다. 개발자가 거짓말을 말할 수 있다는 사실은 어떤 언어로도 막을 수 없습니다. 객체 a를 수정하는 void foo (const A & a)는 배의 수를 반환하는 int countApples (Basket b)와 다르지 않습니다. 어떤 언어로든 컴파일되는 버그 일뿐입니다.
Alessandro Teruzzi 2015 년

55
“피 호출자는 const…를 자유롭게 버릴 수 있습니다.” uhhhhh… (° _ °) 이것은 당신이 여기에서 만들고있는 매우 얕은 논쟁입니다. 수신자는 0xDEADBEEF. 그러나 둘 다 매우 나쁜 프로그래밍 이 될 것이며 현대 컴파일러에 의해 일찍 그리고 신랄하게 잡힐 것입니다. 앞으로 답변을 작성할 때 언어의 백도어를 사용하여 기술적으로 가능한 것과 실제 실제 코드에서 실행 가능한 것을 혼동하지 마십시오. 이와 같은 왜곡 된 정보는 경험이 부족한 독자를 혼란스럽게 만들고 SO에 대한 정보의 품질을 저하시킵니다.
Slipp D. Thompson 2015

8
"Const in C는 지침입니다." 당신이 말하는 것 중 일부에 대한 출처를 인용하면 여기에서 귀하의 사례에 실제로 도움이 될 것입니다. 내 교육 및 업계 경험에서 인용 된 진술은 hogwash입니다. C의 const는 유형 시스템 자체만큼 필수 내장 기능입니다. 둘 다 필요할 때 (실제로 C의 모든 것과 같이) 속일 수있는 백도어가 있지만 예상됩니다. 99.99 %의 코드로 책에서 사용됩니다.
Slipp D. Thompson 2015

29
이 대답은 내가 때때로 C # 디자인을 싫어하는 이유입니다. 언어 디자이너는 그들이 다른 개발자보다 훨씬 더 똑똑하다는 것을 절대적으로 확신하고 버그로부터 과도하게 보호하려고 노력합니다. C ++에서는 메모리에 직접 액세스 할 수 있고 원하는 모든 작업을 수행 할 수 있고 const 선언을 라운드하는 여러 가지 방법이 있기 때문에 const 키워드는 컴파일 타임에서만 작동합니다. 그러나 아무도 정상적인 상황에서 이것을하지 않습니다. 그런데 C #의 'private'및 'protected'는 리플렉션을 사용하여 이러한 메서드 나 필드에 액세스 할 수 있기 때문에 보장 할 수 없습니다.
BlackOverlord

13
@BlackOverlord : (1) 속성이 개발자를 실패보다는 성공으로 자연스럽게 이끄는 언어를 디자인 하려는 욕망 은 디자이너가 고객보다 더 똑똑하다는 믿음이 아니라 유용한 도구를 구축하려는 욕구에서 비롯됩니다 . (2) Reflection은 C # 언어의 일부가 아닙니다. 런타임의 일부입니다. 리플렉션에 대한 액세스는 런타임의 보안 시스템에 의해 제한됩니다. 낮은 신뢰 코드에는 사적인 반사를 수행 할 수있는 기능이 부여되지 않습니다. 보장 액세스 한정자에 의해 제공은위한 낮은 신뢰 코드 ; 높은 신뢰 코드는 무엇이든 할 수 있습니다.
Eric Lippert

24

C #에 const 정확성 이없는 이유 중 하나는 런타임 수준에 존재하지 않기 때문입니다. C # 1.0은 런타임의 일부가 아닌 한 기능이 없었 음을 기억하십시오.

CLR에 const 정확성 개념이없는 몇 가지 이유는 다음과 같습니다.

  1. 이는 런타임을 복잡하게합니다. 게다가, JVM도 가지고 있지 않았고, CLR은 기본적으로 C ++와 같은 런타임이 아닌 JVM과 같은 런타임을 만드는 프로젝트로 시작되었습니다.
  2. 런타임 수준에 const 정확성이있는 경우 BCL에 const 정확성이 있어야합니다. 그렇지 않으면 .NET Framework에 관한 한 기능이 거의 의미가 없습니다.
  3. 그러나 BCL에 const 정확성이 필요한 경우 CLR 위에있는 모든 언어는 const 정확성 (VB, JavaScript, Python, Ruby, F # 등)을 지원해야합니다 . 그런 일은 일어나지 않을 것입니다.

Const 정확성은 거의 C ++에만있는 언어 기능입니다. 따라서 CLR이 확인 된 예외 (Java 언어 전용 기능)를 필요로하지 않는 이유와 동일한 주장으로 귀결됩니다.

또한 이전 버전과의 호환성을 깨지 않고 관리 환경에서 이러한 기본적인 유형 시스템 기능을 도입 할 수 없다고 생각합니다. 따라서 C # 세계에 들어온 const 정확성에 의존하지 마십시오.


JVM에없는 것이 확실합니까? 내가 알기로는 그것을 가지고 있습니다. Java에서 public static void TestMethod (final int val)를 시도 할 수 있습니다.
Incognito

2
Final은 실제로 const가 아닙니다. 값 / 참조 자체가 아니라 참조 IIRC의 필드를 변경할 수 있습니다. (어쨌든 값으로 전달되는 것이기 때문에 매개 변수 값을 변경하지 못하게하는 것은 컴파일러 트릭에
가깝습니다

1
세 번째 총알은 부분적으로 만 사실입니다. BCL 호출에 대한 const 매개 변수를 갖는 것은 단순히 계약을 더 정확하게 설명하기 때문에 주요 변경 사항이 아닙니다. "이 방법은 개체의 상태를 변경하지 않습니다." "이 방법을 사용하려면 const 값을 전달해야합니다."가 깨질 것입니다.
Rune FS

2
그것은 적용됩니다 내부 는 BCL에서 그것을 소개하는 파괴 변화되지 않을 것 있도록하는 방법.
Rune FS

1
런타임이이를 강제 할 수 없더라도 함수가 ref매개 변수 에 쓸 수 있도록 허용 / 예상할지 여부를 지정하는 속성을 갖는 것이 도움이 될 것입니다 (특히, 구조체 메소드 / 속성의 경우 this). 메서드가 ref매개 변수에 쓰지 않을 것이라고 구체적으로 명시하는 경우 컴파일러는 읽기 전용 값이 전달되도록 허용해야합니다. 반대로, 메소드가를 쓸 것이라고 명시 this하면 컴파일러는 이러한 메소드가 읽기 전용 값에서 호출되는 것을 허용하지 않아야합니다.
supercat 2013-07-09

12

C #이 const가 맞지 않는 데에는 두 가지 이유가 있다고 생각합니다.

첫 번째는 이해 가능성 입니다. const 정확성을 이해하는 C ++ 프로그래머는 거의 없습니다. 의 간단한 예 const int arg는 귀엽지 만 char * const * const arg상수가 아닌 문자에 대한 상수 포인터에 대한 상수 포인터 도 보았습니다 . 함수 포인터에 대한 상수 정확성은 완전히 새로운 수준의 난독 화입니다.

두 번째는 클래스 인수가 값으로 전달되는 참조이기 때문입니다. 이것은 명백하게 명확한 구문 없이 처리 할 두 가지 수준의 constness가 이미 있음을 의미 합니다 . 비슷한 걸림돌은 컬렉션 (및 컬렉션 컬렉션 등)입니다.

상수 정확성은 C ++ 유형 시스템의 중요한 부분입니다. 이론적으로는 컴파일시에만 확인되는 것으로 C #에 추가 될 수 있습니다 (CLR에 추가 할 필요가 없으며 const 멤버 메서드 개념이 포함되지 않는 한 BCL에 영향을주지 않음). .

그러나 나는 이것이 가능성이 없다고 생각합니다. 두 번째 이유 (구문)는 해결하기가 매우 어려울 것이며, 이는 첫 번째 이유 (이해성)를 훨씬 더 문제로 만들 것입니다.


1
사람이 사용할 수있는 CLR 정말 이것에 대해 걱정할 필요가 없다는 것을 당신이 맞아요 modoptmodreq(C + / CLI가 이미처럼 - 참조하는 정보를 저장하기 위해 메타 데이터 수준에서 System.Runtime.CompilerServices.IsConst); 그리고 이것은 const 메소드로도 간단하게 확장 될 수 있습니다.
Pavel Minaev

가치가 있지만 형식이 안전한 방식으로 수행되어야합니다. 언급 한 Deep / Shallow const는 전체 웜 캔을 엽니 다.
user1496062 2014

참조가 실제로 조작되는 C ++에서 두 가지 유형의 const를 갖는 것에 대한 귀하의 주장을 볼 수 있습니다. 그러나 C # ( "포인터"를 사용하기 위해 백도어를 통과해야하는 경우)에서는 참조 유형 (즉, 클래스)에 대해 잘 정의 된 의미가 있고 값에 대한 의미는 다르지만 잘 정의되어 있음이 분명해 보입니다. 유형 (즉, 원시, 구조체)
Assimilater

2
const-ness를 이해할 수없는 프로그래머는 C ++로 프로그래밍하면 안됩니다.
Steve White

5

constC #에서 "컴파일 시간 상수"를 의미하며 C ++에서와 같이 "읽기 전용이 아니라 다른 코드에 의해 변경 가능"을 의미합니다. constC #에서 C ++의 대략적인 유사점은 readonly이지만 필드에만 적용 할 수 있습니다. 그 외에도 C #에서 const 정확성에 대한 C ++와 같은 개념은 전혀 없습니다.

많은 잠재적 인 이유가 있기 때문에 그 이유를 확실히 말하기는 어렵지만, 제 생각에는 언어를 가장 먼저 단순하게 유지하고이 두 번째 구현의 불확실한 이점을 유지하고자 할 것입니다.


따라서 MS는 메서드에 const 매개 변수를 갖는 것으로 '노이즈'처럼 간주한다고 생각합니다.
Incognito

1
"노이즈"가 무엇인지 잘 모르겠습니다. 오히려 "높은 비용 / 이익 비율"이라고 부르고 싶습니다. 하지만 물론 C # 팀의 누군가가 확실하게 알려줘야합니다.
Pavel Minaev

그래서 내가 이해했듯이, 당신의 주장은 간단하게 유지하기 위해 constness가 C #에서 제외되었다는 것입니다. 그것에 대해 생각해보십시오.
Steve White

2

이는 C # 7.2 (2017 년 12 월 Visual Studio 2017 15.5 포함)부터 가능합니다.

Structs 및 기본 유형 에만 해당됩니다! , 클래스 구성원이 아닙니다.

in참조에 의한 입력으로 인수를 보내 려면를 사용해야합니다 . 보다:

참조 : https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-parameter-modifier

예를 들어 :

....
static void TestMethod1(in MyStruct val)
{
    val = new MyStruct;  // Error CS8331 Cannot assign to variable 'in MyStruct' because it is a readonly variable
    val.member = 0;  // Error CS8332 Cannot assign to a member of variable 'MyStruct' because it is a readonly variable
}
....
static void TestMethod2(in int val)
{
    val = 0;  // Error CS8331 Cannot assign to variable 'in int' because it is a readonly variable
}
....
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.