看起来C# 3.0的对象初始化语法允许当存在一个无参构造函数时,在构造函数中排除开放/关闭括号。例如:
var x = new XTypeName { PropA = value, PropB = value };
相对于:
var x = new XTypeName() { PropA = value, PropB = value };
我很好奇为什么在 XTypeName
后面构造函数的开闭圆括号是可选的?
看起来C# 3.0的对象初始化语法允许当存在一个无参构造函数时,在构造函数中排除开放/关闭括号。例如:
var x = new XTypeName { PropA = value, PropB = value };
相对于:
var x = new XTypeName() { PropA = value, PropB = value };
我很好奇为什么在 XTypeName
后面构造函数的开闭圆括号是可选的?
那么,为什么您没有在没有对象初始化器的对象创建表达式的默认构造函数调用中也使空括号可选呢?
再看一下上面的标准列表。其中一个是更改不会在程序的词法、语法或语义分析中引入任何新的歧义。您提出的更改确实引入了语义分析歧义:
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
}
}
x = frob 123 + 456;
frob x = 10;
frob x
。毕竟,如果 x
是 int*
,*x = 10;
可以解析并且是合法的。)G(frob + x)
这是指“对x的一元加操作符的结果进行frob处理”还是“将表达式frob添加到x”?
为了解决这些歧义,我们可以引入启发式方法。当您说“var x = 10;”时,这是有歧义的;它可能意味着“推断x的类型”,也可能意味着“x的类型是var”。因此,我们有一个启发式方法:我们首先尝试查找名为var的类型,只有在不存在该类型时才推断x的类型。
或者,我们可以更改语法以使其不含歧义。当设计C# 2.0时,他们就遇到了这个问题:
yield(x);
这是指“在迭代器中yield x”还是“调用带有参数x的yield方法”?通过修改它为
yield return(x);
现在已经没有歧义了。
对于对象初始化程序中可选括号的情况,很容易判断是否引入了歧义,因为可以引入以 { 开头的内容的情况非常少。基本上只有各种语句上下文、语句 lambda、数组初始化程序等。很容易推理出所有情况并证明没有歧义。确保 IDE 保持高效有点困难,但也不会太麻烦。
这种规范的调整通常就足够了。如果是特别棘手的功能,那么我们会使用更重的工具。例如,在设计 LINQ 时,编译器团队和 IDE 团队中都有背景知识的解析器理论人员自行构建了一个解析器生成器,可以分析语法中的歧义,并将查询理解的 C# 语法输入其中;通过这样做,发现了许多查询存在歧义的情况。
或者,在 C# 3.0 中对 lambda 进行高级类型推断时,我们撰写了提案,然后将其发送到剑桥的微软研究院,那里的语言团队足够优秀,能够制定出一份形式化的证明,证明类型推断方案在理论上是正确的。
C# 中是否存在歧义?
当然有。
G(F<A, B>(0))
G( (F<A), (B>0) )
也就是说,它使用两个布尔参数调用了G。在C# 2中,这可能意味着与C# 1中相同的含义,但也可能意味着“将0传递给带有类型参数A和B的通用方法F,然后将F的结果传递给G”。我们向解析器添加了一个复杂的启发式算法,以确定您可能想要的两种情况中的哪一种。
类似地,即使在C# 1.0中,强制转换也是不明确的:
G((T)-x)
那是因为这就是该语言的规定。它们没有任何价值,所以为什么要包含它们呢?
它也非常类似于隐式类型数组。
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
参考资料:http://msdn.microsoft.com/en-us/library/ms364047%28VS.80%29.aspx
我想他们认为,在这种情况下,括号并不是必需的,因为对象初始化器显示了构造和设置对象属性的意图。一个对象创建表达式可以省略构造函数参数列表和括号,只要它包含一个对象或集合初始化器。省略构造函数参数列表和括号相当于指定一个空的参数列表。
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 = "!"};
我不是Eric Lippert,所以不能确定,但我认为这是因为编译器不需要空括号来推断初始化结构。因此它变成了冗余信息,不再需要。