C # 3.0 개체 이니셜 라이저 생성자 괄호가 선택적인 이유는 무엇입니까?
C # 3.0 개체 이니셜 라이저 구문을 사용하면 매개 변수가없는 생성자가있는 경우 생성자에서 괄호 열기 / 닫기 쌍을 제외 할 수 있습니다. 예:
var x = new XTypeName { PropA = value, PropB = value };
반대로 :
var x = new XTypeName() { PropA = value, PropB = value };
왜 생성자 열기 / 닫기 괄호 쌍이 여기에서 선택적인지 궁금합니다 XTypeName
.
이 질문은 2010 년 9 월 20 일에 제 블로그의 주제였습니다 . Josh와 Chad의 답변 ( "값을 추가하지 않아도되는 이유는 무엇입니까?"및 "중복 제거")은 기본적으로 정확합니다. 그것을 조금 더 살리려면 :
객체 이니셜 라이저의 "더 큰 기능"의 일부로 인수 목록을 제거 할 수있는 기능은 "설탕 한"기능에 대한 기준을 충족했습니다. 우리가 고려한 몇 가지 사항 :
- 디자인 및 사양 비용이 낮았습니다
- 어쨌든 객체 생성을 처리하는 파서 코드를 광범위하게 변경하려고했습니다. 매개 변수 목록을 선택적으로 만드는 추가 개발 비용이 더 큰 기능의 비용에 비해 크지 않았습니다.
- 테스트 부담은 더 큰 기능의 비용에 비해 상대적으로 작았습니다
- 문서화 부담은 비교적 작았습니다 ...
- 유지 보수 부담은 적을 것으로 예상되었다; 이 기능이 출시 된 이후 몇 년 동안보고 된 버그는 기억 나지 않습니다.
- 이 기능은이 영역의 향후 기능에 즉각적인 위험을 초래하지 않습니다. (마지막으로해야 할 일은 저렴하고 쉬운 기능을 만드는 것이므로 앞으로 더 매력적인 기능을 구현하기가 훨씬 어렵습니다.)
- 이 기능은 언어의 어휘, 문법 또는 의미 분석에 새로운 모호성을 추가하지 않습니다. 입력하는 동안 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 #의 규칙에 익숙합니다.
새로운 기능을 고려할 때 모호성을 유발하는지 어떻게 판단합니까? 손으로, 공식적인 증거로, 기계 분석으로, 무엇?
세 개 모두. 대부분 위에서 설명한 것처럼 사양과 국수를 살펴 봅니다. 예를 들어 C #에 "frob"라는 새 접두사 연산자를 추가하려고한다고 가정합니다.
x = frob 123 + 456;
(업데이트 : frob
물론입니다 await
. 여기서 분석은 기본적으로 디자인 팀이 추가 할 때 겪은 분석입니다 await
.)
여기서 "frob"는 "new"또는 "++"와 같습니다. 이는 일종의 표현 앞에옵니다. 우리는 원하는 우선 순위와 연관성 등을 해결 한 다음 "프로그램에 이미 유형, 필드, 속성, 이벤트, 메소드, 상수 또는 로컬 frob가 있다면 어떻게됩니까?"와 같은 질문을 시작합니다. 그것은 즉시 다음과 같은 경우로 이어질 것입니다 :
frob x = 10;
does that mean "do the frob operation on the result of x = 10, or create a variable of type frob called x and assign 10 to it?" (Or, if frobbing produces a variable, it could be an assignment of 10 to frob x
. After all, *x = 10;
parses and is legal if x
is int*
.)
G(frob + x)
Does that mean "frob the result of the unary plus operator on x" or "add expression frob to x"?
And so on. To resolve these ambiguities we might introduce heuristics. When you say "var x = 10;" that's ambiguous; it could mean "infer the type of x" or it could mean "x is of type var". So we have a heuristic: we first attempt to look up a type named var, and only if one does not exist do we infer the type of x.
Or, we might change the syntax so that it is not ambiguous. When they designed C# 2.0 they had this problem:
yield(x);
Does that mean "yield x in an iterator" or "call the yield method with argument x?" By changing it to
yield return(x);
it is now unambiguous.
In the case of optional parens in an object initializer it is straightforward to reason about whether there are ambiguities introduced or not because the number of situations in which it is permissible to introduce something that starts with { is very small. Basically just various statement contexts, statement lambdas, array initializers and that's about it. It's easy to reason through all the cases and show that there's no ambiguity. Making sure the IDE stays efficient is somewhat harder but can be done without too much trouble.
This sort of fiddling around with the spec usually is sufficient. If it is a particularly tricky feature then we pull out heavier tools. For example, when designing LINQ, one of the compiler guys and one of the IDE guys who both have a background in parser theory built themselves a parser generator that could analyze grammars looking for ambiguities, and then fed proposed C# grammars for query comprehensions into it; doing so found many cases where queries were ambiguous.
Or, when we did advanced type inference on lambdas in C# 3.0 we wrote up our proposals and then sent them over the pond to Microsoft Research in Cambridge where the languages team there was good enough to work up a formal proof that the type inference proposal was theoretically sound.
Are there ambiguities in C# today?
Sure.
G(F<A, B>(0))
In C# 1 it is clear what that means. It's the same as:
G( (F<A), (B>0) )
That is, it calls G with two arguments that are bools. In C# 2, that could mean what it meant in C# 1, but it could also mean "pass 0 to the generic method F that takes type parameters A and B, and then pass the result of F to G". We added a complicated heuristic to the parser which determines which of the two cases you probably meant.
Similarly, casts are ambiguous even in C# 1.0:
G((T)-x)
Is that "cast -x to T" or "subtract x from T"? Again, we have a heuristic that makes a good guess.
Because that's how the language was specified. They add no value, so why include them?
It's also very similar to implicity typed arrays
var a = new[] { 1, 10, 100, 1000 }; // int[]
var b = new[] { 1, 1.5, 2, 2.5 }; // double[]
var c = new[] { "hello", null, "world" }; // string[]
var d = new[] { 1, "one", 2, "two" }; // Error
Reference: http://msdn.microsoft.com/en-us/library/ms364047%28VS.80%29.aspx
This was done to simplify the construction of objects. The language designers have not (to my knowledge) specifically said why they felt that this was useful, though it is explicitly mentioned in the C# Version 3.0 Specification page:
An object creation expression can omit the constructor argument list and enclosing parentheses, provided it includes an object or collection initializer. Omitting the constructor argument list and enclosing parentheses is equivalent to specifying an empty argument list.
I suppose that they felt the parenthesis, in this instance, were not necessary in order to show developer intent, since the object initializer shows the intent to construct and set the properties of the object instead.
In your first example, the compiler infers that you're calling the default constructor (the C# 3.0 Language Specification states that if no parenthesis are provided, the default constructor is called).
In the second, you explicitly call the default constructor.
You can also use that syntax to set properties while explicitly passing values to the constructor. If you had the following class definition:
public class SomeTest
{
public string Value { get; private set; }
public string AnotherValue { get; set; }
public string YetAnotherValue { get; set;}
public SomeTest() { }
public SomeTest(string value)
{
Value = value;
}
}
All three statements are valid:
var obj = new SomeTest { AnotherValue = "Hello", YetAnotherValue = "World" };
var obj = new SomeTest() { AnotherValue = "Hello", YetAnotherValue = "World"};
var obj = new SomeTest("Hello") { AnotherValue = "World", YetAnotherValue = "!"};
I am no Eric Lippert, so I can't say for sure, but I would assume it is because the empty parenthesis is not needed by the compiler in order to infer the initialization construct. Therefore it becomes redundant information, and not needed.
'Programing' 카테고리의 다른 글
C ++ 열거 형 클래스에 메소드가있을 수 있습니까? (0) | 2020.08.02 |
---|---|
javax.transaction.Transactional 및 org.springframework.transaction.annotation.Transactional (0) | 2020.08.02 |
왜 CURL이 반환되고 오류가 발생합니까? (23) 본문 작성에 실패 했습니까? (0) | 2020.08.02 |
무한 재귀없이 '=='연산자 과부하에서 null을 확인하려면 어떻게합니까? (0) | 2020.08.02 |
Google Maps InfoWindow 스타일링 (0) | 2020.08.02 |