【问题标题】:Property initialization does not call set for List<T>属性初始化不会为 List<T> 调用 set
【发布时间】:2016-10-18 19:08:12
【问题描述】:

我在检查类属性的代码覆盖率时发现了一个场景。当属性的类型为 List 并使用初始化程序时,似乎不会调用 set 方法。这不适用于其他类型,例如字符串和整数。代码覆盖率没有显示 set 调用,也没有遇到 set 中的断点。

示例类:

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() }
};

编辑:我会指出该列表已正确分配,并且可以进行测试,但它似乎绕过了实际的 set 方法。

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

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

【问题讨论】:

  • 初始化它的正确方法是在你的最后一个例子中,我不确定其他版本是什么意思
  • 从未为我分配该列表 - 导致 NullReferenceException。
  • §7.6.10.2 “在等号之后指定集合初始值设定项的成员初始值设定项是嵌入式集合的初始化。不是将新集合分配给字段或属性,而是在初始化器被添加到字段或属性引用的集合中。”您的列表正在其他地方初始化以使您的代码正常工作 - 您给出的示例将无法正确运行。
  • 正如@BoltClock 所指出的,我的问题不包括我正在使用的实际场景,并且我在属性声明中设置了一个新列表的值。

标签: c# initializer


【解决方案1】:

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

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

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

这意味着集合初始化器假定集合已经被实例化(即使在对象初始化器之外也是如此)。请记住,集合初始化器只不过是一系列 .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。

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

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

【讨论】:

  • 你是对的,我错过了我在属性声明之后包含了 = new List() 。你的解释很有道理,谢谢。
【解决方案2】:

你可以这样做:

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} }
};

【讨论】:

  • 在构造函数中初始化足以使问题中的第一个初始化示例工作,无需修改它。和/或示例只是对问题中第二个示例的重述。
  • 是的,我的示例是在没有构造函数的情况下使用的。我会更新以反映它。
猜你喜欢
  • 2021-04-05
  • 1970-01-01
  • 2011-09-17
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多