为什么 C# 3.0 对象初始值设定项构造函数括号是可选的? [英] Why are C# 3.0 object initializer constructor parentheses optional?

查看:38
本文介绍了为什么 C# 3.0 对象初始值设定项构造函数括号是可选的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当存在无参数构造函数时,似乎 C# 3.0 对象初始值设定项语法允许在构造函数中排除开/关括号对.示例:

It seems that the C# 3.0 object initializer syntax allows one to exclude the open/close pair of parentheses in the constructor when there is a parameterless constructor existing. Example:

var x = new XTypeName { PropA = value, PropB = value };

相反:

var x = new XTypeName() { PropA = value, PropB = value };

我很好奇为什么在XTypeName之后构造函数的开/关括号对是可选的?

I'm curious why the constructor open/close parentheses pair is optional here after XTypeName?

推荐答案

这个问题是我 2010 年 9 月 20 日博客的主题.Josh 和 Chad 的回答(它们没有增加任何价值,为什么需要它们?"和消除冗余")基本上是正确的.再充实一点:

This question was the subject of my blog on September 20th 2010. Josh and Chad's answers ("they add no value so why require them?" and "to eliminate redundancy") are basically correct. To flesh that out a bit more:

允许您将参数列表作为更大功能"的一部分省略的功能;的对象初始值设定项符合我们的含糖"标准特征.我们考虑的一些要点:

The feature of allowing you to elide the argument list as part of the "larger feature" of object initializers met our bar for "sugary" features. Some points we considered:

  • 设计和规格成本低
  • 无论如何,我们将大量更改处理对象创建的解析器代码;与更大特征的成本相比,使参数列表可选的额外开发成本并不大
  • 与较大功能的成本相比,测试负担相对较小
  • 相比之下,文档负担相对较小......
  • 预计维护负担很小;我不记得在此功能发布后的几年中报告过任何错误.
  • 该功能不会对该领域的未来功能构成任何直接明显的风险.(我们最不想做的事情是现在制作一个便宜、简单的功能,这使得将来实现更引人注目的功能变得更加困难.)
  • 该功能不会为语言的词汇、语法或语义分析增加新的歧义.它对那种部分程序"没有问题.由 IDE 的IntelliSense"执行的分析;打字时的引擎.等等.
  • 该功能达到了常见的甜蜜点";对于更大的对象初始化功能;通常,如果您使用对象初始值设定项,那正是因为对象的构造函数不允许 允许您设置所需的属性.此类物品简单地是财产袋"是很常见的.首先在构造函数中没有参数.
  • the design and specification cost was low
  • we were going to be extensively changing the parser code that handles object creation anyway; the additional development cost of making the parameter list optional was not large compared to the cost of the larger feature
  • the testing burden was relatively small compared to the cost of the larger feature
  • the documentation burden was relatively small compared...
  • the maintenance burden was anticipated to be small; I don't recall any bugs reported in this feature in the years since it shipped.
  • the feature does not pose any immediately obvious risks to future features in this area. (The last thing we want to do is make a cheap, easy feature now that makes it much harder to implement a more compelling feature in the future.)
  • the feature adds no new ambiguities to the lexical, grammatical or semantic analysis of the language. It poses no problems for the sort of "partial program" analysis that is performed by the IDE's "IntelliSense" engine while you are typing. And so on.
  • the feature hits a common "sweet spot" for the larger object initialization feature; typically if you are using an object initializer it is precisely because the constructor of the object does not allow you to set the properties you want. It is very common for such objects to simply be "property bags" that have no parameters in the ctor in the first place.

为什么在没有具有对象初始值设定项的对象创建表达式的默认构造函数调用中,您不也将空括号设为可选?

Why then did you not also make empty parentheses optional in the default constructor call of an object creation expression that does not have an object initializer?

再看看上面的标准列表.其中之一是更改不会在程序的词汇、语法或语义分析中引入任何新的歧义.您提议的更改确实引入了语义分析歧义:

Take another look at that list of criteria above. One of them is that the change does not introduce any new ambiguity in the lexical, grammatical or semantic analysis of a program. Your proposed change does introduce a semantic analysis ambiguity:

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 行创建 B.M 的一个新实例并调用其默认构造函数.如果第 1 行的括号是可选的,那么第 2 行就会有歧义. 然后我们必须想出一个规则来解决歧义;我们不能让它成为错误,因为那将是一个破坏性更改,将现有的合法 C# 程序更改为损坏的程序.

Line 1 creates a new C, calls the default constructor, and then calls the instance method M on the new object. Line 2 creates a new instance of B.M and calls its default constructor. If the parentheses on line 1 were optional then line 2 would be ambiguous. We would then have to come up with a rule resolving the ambiguity; we could not make it an error because that would then be a breaking change that changes an existing legal C# program into a broken program.

因此,规则必须非常复杂:基本上,括号仅在不引入歧义的情况下才是可选的.我们必须分析所有可能引入歧义的情况,然后在编译器中编写代码来检测它们.

Therefore the rule would have to be very complicated: essentially that the parentheses are only optional in cases where they don't introduce ambiguities. We'd have to analyze all the possible cases that introduce ambiguities and then write code in the compiler to detect them.

有鉴于此,请回过头来看看我提到的所有成本.现在有多少变大了?复杂的规则需要大量的设计、规范、开发、测试和文档成本.复杂的规则更有可能导致未来与功能意外交互的问题.

In that light, go back and look at all the costs I mention. How many of them now become large? Complicated rules have large design, spec, development, testing and documentation costs. Complicated rules are much more likely to cause problems with unexpected interactions with features in the future.

一切为了什么?一个微小的客户利益,不会为语言增加新的表现力,但会增加疯狂的角落案例,只是等着大喊gotcha"在遇到它的某个可怜的毫无戒心的灵魂.诸如此类的功能会立即被删除,并被置于永远不要这样做"的状态.清单.

All for what? A tiny customer benefit that adds no new representational power to the language, but does add crazy corner cases just waiting to yell "gotcha" at some poor unsuspecting soul who runs into it. Features like that get cut immediately and put on the "never do this" list.

你是如何确定这种特殊的歧义的?

How did you determine that particular ambiguity?

那一瞬间就清楚了;我非常熟悉 C# 中用于确定何时需要带点名称的规则.

That one was immediately clear; I am pretty familiar with the rules in C# for determining when a dotted name is expected.

在考虑一项新功能时,您如何确定它是否会引起歧义?手工,形式证明,机器分析,什么?

When considering a new feature how do you determine whether it causes any ambiguity? By hand, by formal proof, by machine analysis, what?

三个.大多数情况下,我们只是看看上面的规格和面条,就像我上面所做的那样.例如,假设我们想向 C# 添加一个名为frob"的新前缀运算符:

All three. Mostly we just look at the spec and noodle on it, as I did above. For example, suppose we wanted to add a new prefix operator to C# called "frob":

x = frob 123 + 456;

(UPDATE:frob当然是await;这里的分析本质上是设计团队在添加await时所经历的分析.)

(UPDATE: frob is of course await; the analysis here is essentially the analysis that the design team went through when adding await.)

frob"这里就像新"一样或++"- 它出现在某种表达之前.我们会计算出所需的优先级和关联性等等,然后开始提出诸如如果程序已经有一个类型、字段、属性、事件、方法、常量或称为 frob 的局部会怎样?"这样的问题.这将立即导致以下情况:

"frob" here is like "new" or "++" - it comes before an expression of some sort. We'd work out the desired precedence and associativity and so on, and then start asking questions like "what if the program already has a type, field, property, event, method, constant, or local called frob?" That would immediately lead to cases like:

frob x = 10;

这是否意味着对 x = 10 的结果进行 frob 操作,或者创建一个名为 x 的 frob 类型的变量并将 10 分配给它?";(或者,如果 frobbing 产生一个变量,它可能是对 frob x 赋值 10.毕竟,*x = 10; 解析并且如果 xint*.)

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)

这是否意味着在 x 上取一元加运算符的结果"?或将表达式 frob 添加到 x"?

Does that mean "frob the result of the unary plus operator on x" or "add expression frob to x"?

等等.为了解决这些歧义,我们可能会引入启发式方法.当你说var x = 10;"时这是模棱两可的;它可能意味着推断 x 的类型";或者它可能意味着x 是 var 类型".所以我们有一个启发式:我们首先尝试查找一个名为 var 的类型,只有当它不存在时,我们才能推断出 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.

或者,我们可能会更改语法,使其不会有歧义.当他们设计 C# 2.0 时,他们遇到了这个问题:

Or, we might change the syntax so that it is not ambiguous. When they designed C# 2.0 they had this problem:

yield(x);

这是否意味着迭代器中的产量 x"?或使用参数 x 调用 yield 方法?"通过将其更改为

Does that mean "yield x in an iterator" or "call the yield method with argument x?" By changing it to

yield return(x);

现在是明确的.

在对象初始值设定项中可选括号的情况下,可以直接推断是否引入了歧义,因为允许引入以 { 开头的内容的情况非常少.基本上只是各种语句上下文、语句 lambda、数组初始值设定项,仅此而已.很容易通过所有案例进行推理并表明没有歧义.确保 IDE 保持高效有点困难,但可以轻松完成.

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.

这种摆弄规范通常就足够了.如果这是一个特别棘手的功能,那么我们会使用更重的工具.例如,在设计 LINQ 时,一名编译器人员和一名 IDE 人员都具有解析器理论背景,他们自己构建了一个解析器生成器,可以分析语法以寻找歧义,然后将提议的 C# 语法输入其中以进行查询理解;这样做发现了许多查询不明确的情况.

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.

或者,当我们在 C# 3.0 中对 lambdas 进行高级类型推断时,我们写下了我们的建议,然后将它们通过池塘发送到剑桥的微软研究院,那里的语言团队足够优秀,可以正式证明类型推理提议在理论上是合理的.

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.

今天的 C# 是否存在歧义?

Are there ambiguities in C# today?

当然.

G(F<A, B>(0))

在 C# 1 中很清楚这意味着什么.是一样的:

In C# 1 it is clear what that means. It's the same as:

G( (F<A), (B>0) )

也就是说,它使用两个布尔值参数调用 G.在 C# 2 中,这可能意味着它在 C# 1 中的含义,但也可能意味着将 0 传递给采用类型参数 A 和 B 的泛型方法 F,然后将 F 的结果传递给 G".我们向解析器添加了一个复杂的启发式方法,用于确定您可能指的是两种情况中的哪一种.

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.

同样,即使在 C# 1.0 中,类型转换也是不明确的:

Similarly, casts are ambiguous even in C# 1.0:

G((T)-x)

是cast -x to T"吗?或从 T 中减去 x"?同样,我们有一个可以很好地猜测的启发式方法.

Is that "cast -x to T" or "subtract x from T"? Again, we have a heuristic that makes a good guess.

这篇关于为什么 C# 3.0 对象初始值设定项构造函数括号是可选的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆