C# 为什么C3.0对象初始值设定项构造函数括号是可选的?

C# 为什么C3.0对象初始值设定项构造函数括号是可选的?,c#,syntax,types,language-design,initializer,C#,Syntax,Types,Language Design,Initializer,似乎C3.0对象初始值设定项语法允许在存在无参数构造函数时排除构造函数中的开/闭括号对。例如: var x = new XTypeName { PropA = value, PropB = value }; 与之相反: var x = new XTypeName() { PropA = value, PropB = value }; 我很好奇为什么构造函数的开/闭括号对在XTypeName之后是可选的?我不是Eric Lippert,所以我不能肯定,但我认为这是因为编译器不需要空括号来推断初

似乎C3.0对象初始值设定项语法允许在存在无参数构造函数时排除构造函数中的开/闭括号对。例如:

var x = new XTypeName { PropA = value, PropB = value };
与之相反:

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

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

我不是Eric Lippert,所以我不能肯定,但我认为这是因为编译器不需要空括号来推断初始化构造。因此,它成为冗余信息,不需要。

因为这就是指定语言的方式。它们没有增加价值,为什么要包括它们

它也非常类似于隐式类型的数组

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

参考:

这样做是为了简化对象的构造。据我所知,语言设计师没有明确说明为什么他们认为这是有用的,尽管在:

对象创建表达式可以省略构造函数参数列表和圆括号,前提是它包含对象或集合初始值设定项。省略构造函数参数列表和括号相当于指定空参数列表


我想他们觉得在这个例子中,括号不是显示开发人员意图所必需的,因为对象初始值设定项显示的是构造和设置对象属性的意图。

在第一个示例中,编译器推断您正在调用默认构造函数。C3.0语言规范规定,如果未提供括号,则调用默认构造函数

在第二种情况下,显式调用默认构造函数

还可以使用该语法设置属性,同时将值显式传递给构造函数。如果您具有以下类定义:

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;
    }
}
所有三项声明均有效:

var obj = new SomeTest { AnotherValue = "Hello", YetAnotherValue = "World" };
var obj = new SomeTest() { AnotherValue = "Hello", YetAnotherValue = "World"};
var obj = new SomeTest("Hello") { AnotherValue = "World", YetAnotherValue = "!"};
。Josh和Chad的答案没有任何价值,为什么需要它们?消除冗余基本上是正确的。要进一步充实这一点:

作为对象初始值设定项更大功能的一部分,允许您省略参数列表的功能满足了我们对甜甜特性的要求。我们考虑了以下几点:

设计和规格成本低 不管怎样,我们将广泛地修改处理对象创建的解析器代码;与较大功能的成本相比,使参数列表成为可选的额外开发成本并不高 与较大功能的成本相比,测试负担相对较小 与……相比,文档负担相对较小。。。 预计维护负担较小;我不记得在这项功能发布后的几年中报告过任何bug。 该功能不会对该领域的未来功能造成任何立即明显的风险。我们现在最不想做的事情就是制作一个便宜、简单的特性,这使得将来实现一个更引人注目的特性变得更加困难。 该功能不会给语言的词汇、语法或语义分析增加新的歧义。它不会对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行创建一个新的B.M实例并调用其默认构造函数。如果第1行的括号是可选的,那么第2行将是不明确的。然后,我们必须制定一个规则来解决这种模糊性;我们不能让它成为一个错误,因为这将是一个突破性的改变,将现有的合法C程序更改为一个突破性的程序

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

在这种情况下,回头看看我提到的所有成本。他们中有多少人现在变大了?复杂的规则具有大的设计、规格、开发 开发、测试和文件成本。复杂的规则更可能导致将来与功能的意外交互出现问题

都是为了什么?这是一个小小的客户利益,它没有给语言增加新的代表性力量,但却增加了疯狂的角落案例,只是等待着对某个遇到它的可怜的、毫无戒心的灵魂大喊“抓住你了”。这类功能会立即被删除,并被列入“永不做此”列表

你是如何确定这种特殊的模糊性的

这一点马上就清楚了;我非常熟悉C中确定何时需要虚线名称的规则

在考虑新功能时,您如何确定它是否会导致任何歧义?通过手工,通过形式证明,通过机器分析,什么

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

x = frob 123 + 456;
更新:frob当然在等待;这里的分析本质上是设计团队在添加wait时进行的分析

这里的frob就像new或++——它出现在某种表达式之前。我们将计算出所需的优先级和关联性等,然后开始问一些问题,比如程序是否已经有了一个名为frob的类型、字段、属性、事件、方法、常量或局部变量?这将立即导致以下情况:

frob x = 10;
这是否意味着对x=10的结果执行frob操作,或者创建一个名为x的frob类型的变量并为其赋值10?或者,如果泡沫产生一个变量,它可以是对泡沫x的赋值10。毕竟,*x=10;解析并在x为int*时合法

这是否意味着frob是x上一元加号运算符的结果,还是将表达式frob添加到x

等等。为了解决这些歧义,我们可以引入启发式。当你说var x=10;这是模棱两可的;这可能意味着推断x的类型,也可能意味着x是var类型。因此我们有一个启发:我们首先尝试查找一个名为var的类型,只有当一个类型不存在时,我们才能推断x的类型

或者,我们可以更改语法,使其不具有歧义性。在设计C2.0时,他们遇到了以下问题:

yield(x);
这是否意味着在迭代器中产生x,或者使用参数x调用yield方法?把它改成

yield return(x);
现在它是明确的

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

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

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

今天的C语言有歧义吗

当然


这是将-x转换为T还是从T中减去x?同样,我们有一个启发式算法,可以很好地进行猜测。

没错,这是多余的,但我只是好奇为什么突然引入它们是可选的?这似乎打破了语言语法的一致性。如果我没有开放的大括号来表示初始值设定项块,那么这应该是illegal语法。有趣的是,你提到Lippert先生,我是在公开寻找他的答案,这样我和其他人都会从一种无聊的好奇中受益。对。在你的例子中的第一个和第二个例子中,它们在功能上是相同的,对吗?@James Dunne-对。这是语言规范指定的部分。空括号是多余的t、 但是你仍然可以提供它们。它们没有增加任何价值,因为它的意图应该是显而易见的,但它与一致性相违背,因为现在我们有两个完全不同的对象构造语法,一个带有必需的括号和逗号分隔的参数表达式,另一个没有。@James Dunne,它实际上与yntax要隐式类型化数组语法,请参见我的编辑。没有类型,没有构造函数,而且意图很明显,因此无需声明。抱歉,我忘了……bat信号方法,虽然看起来很有效,但更适合
IMO是一种直接的联系方式,通过这种方式,人们不会以可索引、可搜索和易于参考的SO帖子的形式获得公共教育所需的公众曝光。我们是否应该直接联系,以编舞一个舞台SO发帖/回复舞蹈我建议你不要发帖子。这对其他可能对这个问题有更多见解的人来说是不公平的。更好的方法是发布问题,然后通过电子邮件发送一个链接,请求参与。@James:我已更新了我的答案,以解决您的后续问题。@Eric,您是否可以在博客上发布此问题?从不做此列表?我很想看到其他永远不会成为C语言一部分的例子:@Eric:我真的很感谢你对我的耐心:谢谢!非常有用。顺便说一句,我们在上周的代码审查中发现了这一点:var list=newlist{};如果有什么东西可以被滥用…@blu这就是我想问这个问题的原因之一。我注意到我们的代码不一致。不一致性通常困扰着我,所以我想我应该看看语法中的可选性背后是否有一个很好的理由可能重复的
yield return(x);
G(F<A, B>(0))
G( (F<A), (B>0) )
G((T)-x)