List <int> test = {1,2,3} - 它是一个功能还是一个bug?

mik*_*tes 49 c# compiler-errors

如您所知,不允许将Array-initialisation语法与Lists一起使用.它会产生编译时错误.例:

List<int> test = { 1, 2, 3} 
// At compilation the following error is shown:
// Can only use array initializer expressions to assign to array types. 
Run Code Online (Sandbox Code Playgroud)

但是今天我做了以下(非常简化):

class Test
{
     public List<int> Field;
}

List<Test> list = new List<Test>
{
    new Test { Field = { 1, 2, 3 } }
};
Run Code Online (Sandbox Code Playgroud)

上面的代码编译得很好,但是在运行时它会给出"对象引用未设置为对象"运行时错误.

我希望该代码能够产生编译时错误.我的问题是:为什么不是,并且有什么好的理由可以让这种情况正确运行?

这已经使用.NET 3.5进行了测试,包括.Net和Mono编译器.

干杯.

Tom*_*cek 47

我认为这是一种设计行为.将Test = { 1, 2, 3 }被编译成一个调用的代码Add存储在该列表的方法Test场.

为什么你得到的理由NullReferenceException是,Testnull.如果将Test字段初始化为新列表,则代码将起作用:

class Test {    
  public List<int> Field = new List<int>(); 
}  

// Calls 'Add' method three times to add items to 'Field' list
var t = new Test { Field = { 1, 2, 3 } };
Run Code Online (Sandbox Code Playgroud)

这是非常合乎逻辑的 - 如果你写,new List<int> { ... }那么它会创建一个新的列表实例.如果不添加对象构造,它将使用现有实例(或null).据我所知,C#规范不包含任何与此场景匹配的显式转换规则,但它给出了一个示例(参见第7.6.10.3节):

List<Contact>可以创建和初始化如下:

var contacts = new List<Contact> {
    new Contact {
        Name = "Chris Smith",
        PhoneNumbers = { "206-555-0101", "425-882-8080" }
    },
    new Contact {
        Name = "Bob Harris",
        PhoneNumbers = { "650-555-0199" }
    }
};
Run Code Online (Sandbox Code Playgroud)

效果与...相同

var contacts = new List<Contact>();
Contact __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
contacts.Add(__c1);
Contact __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
contacts.Add(__c2);
Run Code Online (Sandbox Code Playgroud)

where __c1__c2是临时变量,否则是不可见和不可访问的.

  • 是的,有一点不一致。使用此示例中的逻辑,应该能够编写 `var l = new List&lt;int&gt;(); l = { 1, 2, 3 };` - 但这不起作用! (2认同)

Eri*_*ert 25

我希望该代码能够产生编译时错误.

由于您的期望与规范和实施相反,您的期望将无法实现.

为什么它在编译时不会失败?

因为规范明确规定在7.6.10.2节中是合法的,为方便起见,我在此引用:


在等号后面指定集合初始值设定项的成员初始值设定项是嵌入式集合的初始化.而不是将新集合分配给字段或属性,初始化程序中给出的元素将添加到字段或属性引用的集合中.


什么时候这样的代码运行正确?

正如规范所说,初始化程序中给出的元素被添加到属性引用的集合中.酒店不引用收藏品; 它是null.因此,在运行时它会提供空引用异常.有人必须初始化列表.我建议更改"Test"类,以便其构造函数初始化列表.

什么情景激发了这个功能?

LINQ查询需要表达式而不是语句.将成员添加到新创建的列表中的新创建的集合中需要调用"添加".由于"Add"是返回void的,因此对它的调用只能出现在表达式语句中.此功能允许您创建新集合(使用"new")并填充它,或填充现有集合(不带"new"),其中集合是您作为LINQ结果创建的对象的成员查询.


Jor*_*dão 18

这段代码:

Test t = new Test { Field = { 1, 2, 3 } };
Run Code Online (Sandbox Code Playgroud)

翻译成这个:

Test t = new Test();
t.Field.Add(1);
t.Field.Add(2);
t.Field.Add(3);
Run Code Online (Sandbox Code Playgroud)

因为Field就是null,你得到的NullReferenceException.

这称为集合初始值设定项,如果您执行此操作,它将在您的初始示例中起作用:

List<int> test = new List<int> { 1, 2, 3 };
Run Code Online (Sandbox Code Playgroud)

你真的需要新的东西才能使用这种语法,即集合初始化器只能出现在对象创建表达式的上下文中.在C#规范的7.6.10.1节中,这是对象创建表达式的语法:

object-creation-expression:
  new type ( argument-list? ) object-or-collection-initializer?
  new type object-or-collection-initializer
object-or-collection-initializer:
  object-initializer
  collection-initializer
Run Code Online (Sandbox Code Playgroud)

所以这一切都从一个new表达开始.在表达式中,您可以使用不带new(第7.6.10.2节)的集合初始值设定项:

object-initializer:
  { member-initializer-list? }
  { member-initializer-list , }
member-initializer-list:
  member-initializer
  member-initializer-list , member-initializer
member-initializer:
  identifier = initializer-value
initializer-value:
  expression
  object-or-collection-initializer // here it recurses
Run Code Online (Sandbox Code Playgroud)

现在,你真正缺少的是某种列表文字,这将非常方便.我在这里为可枚举提出了一个这样的文字.