이 질문은 2010 년 9 월 20 일 제 블로그의 주제였습니다 . Josh와 Chad의 답변 ( "그들은 가치를 추가하지 않는데 왜 필요한가?"및 "중복성을 제거하기 위해")은 기본적으로 정확합니다. 좀 더 구체화하려면 :
객체 이니셜 라이저의 "큰 기능"의 일부로 인수 목록을 제거 할 수있는 기능은 "sugary"기능에 대한 기준을 충족했습니다. 우리가 고려한 몇 가지 사항 :
- 설계 및 사양 비용이 낮았습니다.
- 어쨌든 객체 생성을 처리하는 파서 코드를 광범위하게 변경하려고했습니다. 매개 변수 목록을 선택적으로 만드는 추가 개발 비용은 더 큰 기능의 비용에 비해 크지 않았습니다.
- 더 큰 기능의 비용에 비해 테스트 부담이 상대적으로 적음
- 문서화 부담이 상대적으로 적었습니다 ...
- 유지 관리 부담이 적을 것으로 예상되었습니다. 이 기능이 출시 된 이후 몇 년 동안보고 된 버그는 기억 나지 않습니다.
- 이 기능은이 영역의 향후 기능에 즉각적으로 명백한 위험을 초래하지 않습니다. (마지막으로하고 싶은 것은 지금 저렴하고 쉬운 기능을 만드는 것입니다. 이렇게하면 앞으로 더 매력적인 기능을 구현하기가 훨씬 더 어려워집니다.)
- 이 기능은 언어의 어휘, 문법 또는 의미 분석에 새로운 모호성을 추가하지 않습니다. 입력하는 동안 IDE의 "IntelliSense"엔진이 수행하는 일종의 "부분 프로그램"분석에는 문제가 없습니다. 등등.
- 이 기능은 더 큰 객체 초기화 기능에 대해 공통적 인 "스위트 스팟"에 도달합니다. 객체의 생성자가 없기 때문에이 정확히 초기화는 객체를 사용하는 일반적 경우 하지 원하는 속성을 설정할 수 있습니다. 이러한 오브젝트는 애초에 ctor에 매개 변수가없는 "속성 가방"이되는 것이 매우 일반적입니다.
그렇다면 객체 이니셜 라이저 가 없는 객체 생성 표현식의 기본 생성자 호출에서 빈 괄호도 선택 사항으로 만들지 않은 이유는 무엇 입니까?
위의 기준 목록을 다시 살펴보십시오. 그중 하나는 변경으로 인해 프로그램의 어휘, 문법 또는 의미 분석에 새로운 모호성이 도입되지 않는다는 것입니다. 귀하의 제안 된 변경은 하지 의미 론적 분석 모호성을 소개 :
class P
{
class B
{
public class M { }
}
class C : B
{
new public void M(){}
}
static void Main()
{
new C().M(); // 1
new C.M(); // 2
}
}
1 행은 새 C를 만들고 기본 생성자를 호출 한 다음 새 개체에서 인스턴스 메서드 M을 호출합니다. 2 행은 BM의 새 인스턴스를 만들고 기본 생성자를 호출합니다. 1 행의 괄호가 선택 사항 인 경우 2 행은 모호합니다. 그런 다음 모호성을 해결하는 규칙을 만들어야합니다. 기존의 합법적 인 C # 프로그램을 손상된 프로그램으로 변경하는 주요 변경 사항이므로 오류로 만들 수 없습니다 .
따라서 규칙은 매우 복잡해야합니다. 본질적으로 괄호는 모호성을 도입하지 않는 경우에만 선택 사항입니다. 모호성을 유발하는 모든 가능한 경우를 분석 한 다음이를 감지하기 위해 컴파일러에서 코드를 작성해야합니다.
그런 관점에서 돌아가서 내가 언급 한 모든 비용을 살펴보십시오. 이제 얼마나 많이 커지나요? 복잡한 규칙에는 설계, 사양, 개발, 테스트 및 문서화 비용이 많이 듭니다. 복잡한 규칙은 향후 기능과의 예기치 않은 상호 작용에 문제를 일으킬 가능성이 훨씬 더 높습니다.
무엇을 위해? 언어에 새로운 표현력을 추가하지 않는 작은 고객 혜택이지만, 그 언어에 부딪 치는 의심하지 않는 일부 영혼에게 "gotcha"라고 소리를 지르기 위해 기다리는 미친 코너 케이스를 추가합니다. 이와 같은 기능은 즉시 제거 되고 "절대하지 않음"목록에 포함됩니다.
특정 모호성을 어떻게 결정 했습니까?
그것은 즉시 분명했습니다. 점으로 구분 된 이름이 예상되는시기를 결정하기위한 C #의 규칙에 대해 잘 알고 있습니다.
새로운 기능을 고려할 때 모호성을 유발하는지 여부를 어떻게 결정합니까? 손으로, 공식적인 증명으로, 기계 분석으로 무엇을?
세 개 모두. 대부분 우리는 위에서 한 것처럼 사양과 국수를 봅니다. 예를 들어 "frob"이라는 새 접두사 연산자를 C #에 추가하려고한다고 가정합니다.
x = frob 123 + 456;
(업데이트 : frob
당연히 await
; 여기서 분석은 본질적으로 디자인 팀이를 추가 할 때 겪은 분석입니다 await
.)
여기에서 "frob"은 "new"또는 "++"와 같습니다. 일종의 표현 앞에옵니다. 원하는 우선 순위와 연관성 등을 파악한 다음 "프로그램에 이미 유형, 필드, 속성, 이벤트, 메서드, 상수 또는 frob이라는 로컬이있는 경우 어떻게됩니까?"와 같은 질문을 시작합니다. 그러면 다음과 같은 경우가 즉시 발생합니다.
frob x = 10;
"x = 10의 결과에 대해 frob 연산을 수행하거나 x라는 frob 유형의 변수를 생성하고 여기에 10을 할당합니까?"라는 의미입니까? (또는 frobbing이 변수를 생성하는 경우 10에 대한 할당이 될 수 있습니다 frob x
. 결국 *x = 10;
구문 분석을 수행하고이면 합법적 x
입니다 int*
.)
G(frob + x)
"x에 대한 단항 더하기 연산자의 결과를 frob"또는 "x에 표현식 frob 추가"를 의미합니까?
등등. 이러한 모호함을 해결하기 위해 휴리스틱을 도입 할 수 있습니다. "var x = 10;"이라고 말하면 그것은 모호합니다. "x의 유형을 추론"하거나 "x가 var 유형입니다"를 의미 할 수 있습니다. 그래서 우리는 휴리스틱을 가지고 있습니다. 먼저 var라는 유형을 찾아 보려고 시도하고, 존재하지 않는 경우에만 x 유형을 추론합니다.
또는 모호하지 않도록 구문을 변경할 수 있습니다. C # 2.0을 설계 할 때 다음과 같은 문제가있었습니다.
yield(x);
이것은 "반복기에서 x를 산출"또는 "인수 x로 yield 메서드를 호출"을 의미합니까? 그것을 변경함으로써
yield return(x);
이제 모호하지 않습니다.
객체 이니셜 라이저의 선택적 괄호의 경우 {로 시작하는 것을 도입하는 것이 허용되는 상황의 수가 매우 적기 때문에 도입 된 모호성이 있는지 여부를 추론하는 것이 간단합니다 . 기본적으로 다양한 문 컨텍스트, 문 람다, 배열 이니셜 라이저 및 그게 전부입니다. 모든 경우를 추론하고 모호함이 없음을 보여주는 것은 쉽습니다. IDE의 효율성을 유지하는 것은 다소 어렵지만 너무 많은 문제없이 수행 할 수 있습니다.
이러한 종류의 사양을 다루면 일반적으로 충분합니다. 특히 까다로운 기능이라면 더 무거운 도구를 꺼냅니다. 예를 들어 LINQ를 설계 할 때 컴파일러 전문가 중 한 명과 파서 이론에 대한 배경 지식이있는 IDE 전문가 중 한 명이 모호성을 찾는 문법을 분석 할 수있는 파서 생성기를 구축 한 다음 쿼리 이해를 위해 제안 된 C # 문법을 입력했습니다. ; 이렇게하면 쿼리가 모호한 경우가 많이 발견되었습니다.
또는 C # 3.0에서 람다에 대한 고급 유형 추론을 수행 할 때 제안서를 작성한 다음이를 연못을 통해 케임브리지의 Microsoft Research로 보냈습니다. 그곳에서 언어 팀은 유형 추론 제안이 다음과 같다는 공식적인 증거를 작성하기에 충분했습니다. 이론적으로 건전합니다.
오늘날 C #에 모호한 부분이 있습니까?
확실한.
G(F<A, B>(0))
C # 1에서는 그것이 의미하는 바가 분명합니다. 다음과 같습니다.
G( (F<A), (B>0) )
즉, bool 인 두 개의 인수로 G를 호출합니다. C # 2에서는 C # 1에서 의미하는 바를 의미 할 수 있지만 "형식 매개 변수 A와 B를 사용하는 일반 메서드 F에 0을 전달한 다음 F의 결과를 G에 전달"을 의미 할 수도 있습니다. 파서에 복잡한 휴리스틱을 추가하여 두 가지 경우 중 어떤 것을 의미하는지 결정했습니다.
마찬가지로 C # 1.0에서도 캐스트가 모호합니다.
G((T)-x)
"cast -x to T"또는 "subtract x from T"입니까? 다시, 우리는 좋은 추측을하는 휴리스틱을 가지고 있습니다.