为什么不允许使用重新分配的集合初始值设定项?

我一直认为它在两方面都很好。 然后做了这个测试并意识到它不允许重新分配:

int[] a = {0, 2, 4, 6, 8}; 

工作正常但不是:

 int [ ] a; a = { 0, 2, 4, 6, 8 }; 

这有什么技术原因吗? 我想我会在这里问一下,因为这种行为是我直觉所期待的。

首先,我们让条款正确。 那不是集合初始化程序。 那是一个数组初始化器 。 集合初始值设定项始终遵循集合类型的构造函数。 数组初始值设定项仅在本地或字段声明初始值设定项或数组创建表达式中合法。

你完全正确地注意到这是一个奇怪的规则。 让我准确地描述它的古怪:

假设您有一个采用一组int的方法M. 所有这些都是合法的:

 int[] x = new[] { 10, 20, 30 }; int[] y = new int[] { 10, 20, 30 }; int[] z = new int[3] { 10, 20, 30 }; M(new[] { 10, 20, 30 }); M(new int[] { 10, 20, 30 }); M(new int[3] { 10, 20, 30 }); 

 int[] q = {10, 20, 30}; // legal! M( { 10, 20, 30 } ); // illegal! 

看起来要么“孤独的”数组初始化程序应该在任何地方都是合法的,而“装饰”的数组初始化程序应该是合法的。 奇怪的是,这个伪表达式只在初始化器中有效,而不是表达式合法的其他任何地方。

在我批评和捍卫这一选择之前,我想说,首先,这种差异是一次历史性事故。 它没有令人信服的好理由。 如果我们可以在不破坏代码的情况下摆脱它,我们会。 但我们做不到。 如果我们今天再次从头开始设计C#,我认为没有“new”的“孤独”数组初始化程序不是一个有效的语法。

所以,让我首先说明为什么不应该允许数组初始值设定项作为表达式,并且应该允许在局部变量初始值设定项中使用。 然后我会给出相反的理由。

不应允许数组初始值设定项作为表达式的原因:

数组初始化程序违反了良好的属性{始终意味着引入新的代码块。 在您键入时解析的IDE中的错误恢复解析器喜欢使用大括号作为判断语句何时不完整的便捷方式; 如果你看到:

 if (x == M( { Q( 

然后,代码编辑器很容易猜到你在{ 。 编辑将假设Q(是一个陈述的开头而且它没有结束。

但是如果数组初始值设定项是合法的表达式,那么可能缺少的是Q )})){}

其次,数组初始值设定项作为表达式违反了所有堆分配在其中具有“新”的良好原则。

在字段和本地初始值设定项中应允许数组初始值设定项的原因:

请记住,在隐式类型化本地,匿名类型或数组类型推断之前,数组初始值设定项已添加到v1.0中的语言中。 回到那天我们没有令人愉快的“new [] {10,20,30}”语法,所以没有数组初始值设定项,你不得不说:

 int[] x = new int[] { 10, 20, 30 }; 

这看起来很多余! 我可以看出为什么他们想要从那里获得“new int []”。

当你说

 int[] x = { 10, 20, 30 }; 

它在语法上不明确; 解析器知道这是一个数组初始化器,而不是代码块的开头(与我上面提到的情况不同。)也不是类型模糊的; 很明显,初始化器是来自上下文的一组int。

因此,该论证certificate了为什么在C#1.0中,数组初始值设定项在本地和字段初始值设定项中是允许的,而在表达式上下文中则不允许。

但那不是我们今天所处的世界。 如果我们今天从头开始设计这个,我们可能没有没有“new”的数组初始化器。 现在我们当然意识到更好的解决方案是:

 var x = new[] { 10, 20, 30 }; 

并且该表达式在任何上下文中都有效。 如果您认为合适,可以在= “声明”侧或“初始化程序”侧明确键入它,或者您可以让编译器推断出任何一方或两者的类型。

总而言之,是的,你是对的,数组初始化器只能在本地和字段声明中,而不能在表达式上下文中。 十年前有一个很好的理由,但在现代世界中,类型推理已经不再是一个很好的理由了。 在这一点上,这只是一次历史性事故。

最多的是:

  int [] a;// ={1,2,3,4}; a = new [] {1, 2, 3, 4}; 

在VB中工作方式与声明相同,更容易xD

 Dim a() As Integer '={1,2,3,4} a = {1, 2, 3, 4}