接口集合成员在对象初始化期间的奇怪行为

本文关键字:成员 集合 对象 初始化 接口 | 更新日期: 2023-09-27 18:04:45

我在以下代码中遇到运行时NullReferenceException异常:

public class Container
{
    public IList<string> Items { get; set; }
}
class Program
{
    static void Main(string[] args)
    {
        var container = new Container() { Items = {"Test"} };
    }
}

这是合乎逻辑的,编译器不能创建接口实例,但我得到一个运行时异常,而不是编译时间。当我进一步研究这种行为时,我更加困惑了:

    var container = new Container() { Items = {} }; //Legal, Items is null after initialization
    var container = new Container() { Items = { "Test" } }; //Legal, throws exception
    container.Items = {}; //Illegal doesn't compile
    container.Items = {"Test"}; //Illegal doesn't compile

这是某种bug还是我没理解什么?我用的是。net framework 4.0

接口集合成员在对象初始化期间的奇怪行为

它编译,因为编译器不知道List已经在其他地方初始化。您可以通过在构造函数中添加初始化来使它工作:

public class Container
{
    public IList<string> Items { get; set; }
    public Container()
    {
        Items = new List<string>();
    }
}

或更改属性以隐藏字段,该字段在创建类实例时初始化:

private IList<string> items = new List<string>();
public IList<string> Items
{
    get { return items; }
    set { items = value; }
}

那么,var container = new Container() { Items = { "Test" } };工作得很好。

运行时对集合初始化组中的每个项调用.Add()方法。当属性没有初始化为new List<string>时,它的值是null,这就是为什么会抛出NullReferenceException

对象和集合初始化式(c#编程指南)

通过使用集合初始化式,您不必指定多个在源代码中调用类的Add方法;编译器添加调用

您没有初始化List

  var container = new Container() { Items = new List<string>() { "Test" } };

顺便说一下,下面是合法的,因为编译器没有任何问题(语法是正确的,等等)

var container = new Container() { Items = {} }; 

但是因为编译器不知道Items列表没有被初始化(你没有在集合初始化器{}中传递任何项),.Add方法不会在List上被调用,并且运行时不会知道Items对象是空的

另一方面,下面的代码对编译器来说是合法的,但是它会在运行时抛出一个异常,因为你试图初始化传递一个项的列表(这对编译器来说是正确的,原因与上面解释的相同),所以当运行时调用.Add方法时,它会抛出一个空引用异常,因为Items还没有初始化

 var container = new Container() { Items = { "Test" } };