属性 初始化不为 List<T> 调用 set

Property initialization does not call set for List<T>

我在检查 class 的属性的代码覆盖率时发现了一个场景。当 属性 的类型为 List< T >,并且使用了初始值设定项时,似乎没有调用 set 方法。对于其他类型,例如字符串和整数,情况并非如此。代码覆盖率不显示 set 调用,也不命中 set 中的断点。

示例class:

public class ContainerClass
{
    public string Text { get; set; }
    public List<Item> Items { get; set; }
}

当使用初始化器时,如下所示,Text 上的 set 方法被调用,并在代码覆盖率中注册,但 Items 上的 set 方法没有,我想知道为什么:

var arrange = new ContainerClass
{
    Text = "value",
    Items = { new Item() }
};

编辑:我要指出的是列表已正确分配,并且可以对其进行测试,但它似乎绕过了实际的设置方法。

有趣的是,当我指定新列表时,它确实被调用了:

var arrange = new ContainerClass
{
    Items = new List<Item> { new Item() }
};

您可以执行以下任一操作:

public class ContainerClass
{
    public string Text { get; set; }
    public List<Item> Items { get; set; }

    public ContainerClass()
    {
        Items = new List<Item>();
    }
}

然后:

var arrange = new ContainerClass
{
    Text = "SomeValue"
};
arrange.Items.Add(new Item(){Prop=Value});

或者,如果您不按照说明使用构造函数,您可以像这样初始化一个列表:

var arrange = new ContainerClass
{
    Items = new List<Item>(){ new Item(){Prop=Value} }
};

When using an initializer, like below, the set method on Text is called, and registers in code coverage, but the set method on Items does not, and I am wondering why:

那是因为在调用集合初始化程序时,Items 列表实际上并未被初始化。来自规范的第 7.6.10.2 节:

A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the field or property, the elements given in the initializer are added to the collection referenced by the field or property.

这意味着集合初始化器假设集合已经被实例化(即使在对象初始化器之外也是如此)。请记住,集合初始化程序只不过是一系列 .Add() 调用。

为了使您的代码 运行 没有错误,项目必须已经初始化。这可能发生在 属性 声明本身(假设它是一个自动实现的 属性):

public class ContainerClass
{
    public string Text { get; set; }
    public List<Item> Items { get; set; } = new List<Item>();
}

或者在构造函数中(你还没有显示):

public class ContainerClass
{
    public string Text { get; set; }
    public List<Item> Items { get; set; }

    public ContainerClass()
    {
        Items = new List<Item>();
    }
}

否则,您的代码将在程序尝试计算集合初始值设定项时抛出 NullReferenceException。

根据您关于 setter 永远不会被 Items 调用的说法,它很可能是在 属性 声明(或支持字段,如果它实际上不是自动实现的 属性).如果它在构造函数中初始化,setter 仍然会被调用。

您的第二个示例导致 setter 被调用,因为您确实正在为那里的项目分配一个新列表。