为什么不允许在重新赋值时使用集合初始化式

本文关键字:集合 初始化 赋值 不允许 新赋值 为什么 | 更新日期: 2023-09-27 18:18:39

我一直认为这两种方式都很好。然后做了这个测试,发现它在重新赋值时是不允许的:

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

可以,但不能:

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

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

为什么不允许在重新赋值时使用集合初始化式

首先,让我们把术语弄正确。这不是集合初始化式。这是一个数组初始化器。集合初始化项总是跟在集合类型的构造函数之后。数组初始化式仅在局部或字段声明初始化式中合法,或在数组创建表达式中合法。

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

假设你有一个方法M,它接受一个int型数组。这些都是合法的:

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的"lone"数组初始化器将不是有效语法的可能性很大。

那么,让我首先给出一些原因,为什么数组初始化式不应该被允许作为表达式,而应该被允许在局部变量初始化式中使用。然后我会给出一些相反的理由。

数组初始化式不允许作为表达式的原因:

数组初始化项违反了{总是意味着引入一个新的代码块的nice属性。IDE中的错误恢复解析器在您键入时进行解析,喜欢使用大括号作为一种方便的方式来判断语句何时不完整;如果你看到:

if (x == M(
{ 
   Q(

那么代码编辑器很容易猜到您在{之前缺少))。编辑器将假定Q(是语句的开头,而缺少语句的结尾。

但是如果数组初始化式是合法表达式,那么可能缺少的是)})){} 紧跟在之后的Q .

第二,数组初始化式作为表达式违反了所有堆分配中都有"new"的好原则。

在字段和局部初始值设定项中允许数组初始值设定项的原因:

请记住,在v1.0版本中,在隐式类型化局部变量、匿名类型或数组类型推断之前,数组初始化器被添加到语言中。在过去,我们没有令人愉快的"new[]{10,20,30}"语法,所以如果没有数组初始化器,您必须说:

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

看起来很多余!我可以理解为什么他们想把"new int[]"去掉。

当你说

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

在语法上没有歧义;解析器知道这是一个数组初始化器,而不是代码块的开始(不像我上面提到的情况)。它也不是类型二义的;很明显,初始化式是一个来自上下文的int型数组。

所以这个参数解释了为什么在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}