【问题标题】:Why does collection initializer work with getter-only property?为什么集合初始值设定项仅适用于 getter 属性?
【发布时间】:2016-06-26 19:21:36
【问题描述】:

这对我来说是非常不可预测的代码结果。

没想到这段代码会产生这样的结果。

所以,我阅读了 Jeffrey Richter 的书 (clr ia c#),并且有一个包含此代码的示例。

internal class ClassRoom
{
    private List<String> _students = new List<string>();
    public List<String> Students { get { return _students; } }
}

ClassRoom classRoom = new ClassRoom
{
    Students = {"Mike", "Johny", "Vlad", "Stas"}
};

foreach (var some in classRoom.Students)
{
    Console.WriteLine(some);
}

而且,正如你们中的一些人建议的那样,我们可以在控制台中看到四个名称。 所以,我的问题是:

我们如何获得我们的名字,如果他们要分别在学生和 _students 中,由于不存在学生设置器而没有分配。

__________EDIT_1_______________

我阅读了您的回答,并感谢您的回答。

但是,你知道,这是一种误解...... 实在想不通。

当我初始化 Students 时,我会初始化 _students 吗?

同样重要的是,当我从 IEnumerable(some) 获取变量时, 我从 Students 中获取变量? 我认为不会。

我不明白,我究竟在哪里分配变量(INITALIZE)_students 集合?

谢谢你帮我弄清楚!

【问题讨论】:

  • 通过添加东西到列表中。
  • 是的。当我们取“一些”变量时,我们从 _students 取变量,不是吗?

标签: c# properties clr getter-setter


【解决方案1】:

这些其他答案几乎是正确的。让我们通过它,真正做到一切。

为什么集合初始值设定项只能与 getter 属性一起使用?

因为属性值没有改变。属性所引用的集合的内容是被改变的。

将财产想象成一个桶。您无法更改属性引用的存储桶,但您可以更改存储桶的内容。

我们如何获得我们的名字,如果他们要分别在学生和 _students 中,由于不存在学生设置器而没有分配。

根本不会分配 Students 属性。怎么会这样?它没有二传手。

名称被添加到学生 getter 引用的集合中。

当我初始化学生时,我初始化 _students ?

没有。 _students的初始化是给_students赋值的那一行。

集合初始化器将值添加到现有集合。变量_students已经被初始化了。

当我从 IEnumerable(some) 获取变量时,我从 Student 获取变量?

您不会从任何事物中获取变量。您采用。变量不是值;变量包含值。不要将存储与存储的值混淆。

我不明白,我究竟在哪里分配变量(INITALIZE)_students 集合?

_students 变量在初始化 _students 的行中被初始化。集合的内容在包含内容的行中初始化。

也许按照发生的顺序列出所有发生的事情会有所帮助:

  • 创建了一个新的教室对象; _students 字段为空;产生对该对象的引用。
  • 创建了一个新的空列表;对列表的引用被复制到新创建的 Classroom 对象的 _students 字段中。
  • Student getter 在对新 Classroom 对象的引用上被调用。它返回 _students 的内容,这是对空列表的引用。
  • 四个项目被添加到该列表中
  • 对 Classroom 对象的引用被复制到教室变量中。

或者,在代码中,这相当于:

c = create a Classroom
c._students = null
l1 = create a List<string>
c._students = l1
l2 = c.Students -- which returns c._students
l2.Add("...")  -- four times
classRoom = c

你看,从来没有一个学生的 setter 被调用。没有必要。它所做的只是获取 _students 中包含的引用,该引用已经被字段初始化器初始化。

【讨论】:

    【解决方案2】:

    通过说Students = {"Mike", "Johny", "Vlad", "Stas"}它正在做的是

    property Students 返回底层 List&lt;String&gt; _students 实例,并且您正在使用集合初始值设定项添加字符串文字。

    所以,当我遍历 Student 时,我是否遍历了 _学生?

    技术上是的,您的 _students 是一个 private 变量,Students 属性是该私有字段的包装器,以便外部消费者可以访问该列表。

    所以当您说Students = ... 时,您实际上是在设置或初始化私有字段,因为访问Students 会返回List&lt;string&gt; 实例。

    【讨论】:

    • 那么,当我遍历学生时,我是否遍历了 _students ?
    • 谢谢!我会考虑的。
    【解决方案3】:

    _students 字段在构造时在内部分配一次,缺少公共设置器会阻止它从外部设置为不同的列表实例。 ClassRoom 的对象初始化器实际上并没有设置 Students 集合属性,而是简单地利用集合初始化器语法将初始项添加Students...

    ClassRoom classRoom = new ClassRoom
    {
        Students = { "Mike", "Johny", "Vlad", "Stas" }
    };
    

    上述内容与以下内容类似,但不完全相同,因为对象初始化程序版本在填充集合之前不会执行对 classRoom 的赋值。

    ClassRoom classRoom = new ClassRoom();
    classRoom.Students.Add("Mike");
    classRoom.Students.Add("Johny");
    classRoom.Students.Add("Vlad");
    classRoom.Students.Add("Stas");
    

    相反,请注意,由于缺少公共设置器,以下将产生编译器错误,因此访问确实受到预期的限制......

    ClassRoom classRoom = new ClassRoom
    {
        Students = new List<string> { "Mike", "Johny", "Vlad", "Stas" }
    }
    

    【讨论】:

    • 这个答案几乎是正确的。对classRoom 的分配发生在列表被填满之后,而不是之前。例如,如果其中一个 Add 引发异常,classRoom 仍将未分配。
    • @EricLippert,如此真实......我编辑了我的答案以反映这一点。谢谢。
    猜你喜欢
    • 2011-08-01
    • 1970-01-01
    • 2014-03-02
    • 2011-04-10
    • 2018-07-22
    • 2020-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多