C# 只读集合属性的对象初始化

18

我很难理解下面这段 C# 代码示例的意思。测试类的集合(List)属性被设置为只读,但是在对象初始化器中似乎可以对其进行赋值。

** 编辑:已修复 List 'getter' 的问题

using System;
using System.Collections.Generic;
using NUnit.Framework;

namespace WF4.UnitTest
{
    public class MyClass
    {
        private List<string> _strCol = new List<string> {"test1"};

        public List<string> StringCollection 
        {
            get
            {
                return _strCol;
            }
        }
    }

    [TestFixture]
    public class UnitTests
    {
        [Test]
        public void MyTest()
        {
            MyClass c = new MyClass
            {
                // huh?  this property is read only!
                StringCollection = { "test2", "test3" }
            };

            // none of these things compile (as I wouldn't expect them to)
            //c.StringCollection = { "test1", "test2" };
            //c.StringCollection = new Collection<string>();

            // 'test1', 'test2', 'test3' is output
            foreach (string s in c.StringCollection) Console.WriteLine(s);
        }
    }
}

3
那个不寻常的“get”会引起困惑,顺便说一下... - Marc Gravell
4个回答

31

This:

MyClass c = new MyClass
{
    StringCollection = { "test2", "test3" }
};

被翻译成:

MyClass tmp = new MyClass();
tmp.StringCollection.Add("test2");
tmp.StringCollection.Add("test3");
MyClass c = tmp;

从未尝试调用setter - 它只是在调用getter的结果上调用Add。请注意,它也不会清除原始集合。
这在C# 4规范的7.6.10.3节中有更详细的描述。
编辑:作为一个有趣的点,我有点惊讶它会调用getter两次。我预期它会调用getter一次,然后调用Add两次...规范包括一个演示的例子。

谢谢你指出了C#规范的那一部分。阅读后,我可以看到那个语法是如何定义的。就我个人而言,我不明白为什么他们要允许那种语法(看起来像是将一组新元素分配给属性)...或许是因为我最近写了太多JavaScript :) - markdb314
3
它之所以存在,是因为它非常有用。允许调用者操作集合而不是替换集合并不是什么不寻常的事情。 - Jon Skeet
你解释了为什么那段不寻常的代码可以工作,但我仍然不明白为什么被注释掉的第一行代码不能工作。根据你的说明,我会期望 c.StringCollection = {"test1", "test2"}; 被允许并且能够达到同样的效果。但是原帖作者说这样编译不通过。 - Jesse Webb
1
@JesseWebb:集合初始化器只允许在对象创建表达式中使用,可以直接在新创建的集合上使用,例如 new List<string> { "x", "y", "z" } 或嵌入到对象初始化器中。c.StringCollection = { "test1", "test2" } 只是一个赋值语句 - 在那里没有对象创建表达式。 - Jon Skeet

13

您没有调用setter,实际上每次都是在调用c.StringCollection.Add(...)(对于“test2”和“test3”) - 这是一个集合初始化。要使其成为属性赋值,应该是:

// this WON'T work, as we can't assign to the property (no setter)
MyClass c = new MyClass
{
    StringCollection = new StringCollection { "test2", "test3" }
};

1
@remi - 看一下 get 方法;每次调用 get 方法,它都会返回一个只包含 "test1" 的集合。 - Marc Gravell
@markdb314 我已经回答过了... 每次你调用那个 get 方法,你的代码都会创建一个新的列表。你应该将列表作为类的字段。 - Marc Gravell
如果你实际上使用一个字段来支持你的属性(该字段可以通过另一个集合初始化器进行初始化),那么你最终会得到一个更一致的属性……而不是在每次getter调用时创建一个新的列表。 - Jon Skeet
公共类MyClass { private List<String> 列表 = new List<string>() { "测试1" }; public List<string> 字符串集合 { get { return 列表; } } } - Nario
1
@markdb314:因为集合初始化器只有作为对象创建表达式的一部分才有效(C#规范第7.6.10节)。 - Jon Skeet
显示剩余5条评论

0

我认为,由于是只读的,你无法进行操作

c.StringCollection = new List<string>();

但是你可以将项目分配给列表...
我错了吗?


-1

StringCollection 属性没有 setter,所以除非你添加一个,否则无法修改其值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接