只读的List<T>,私有set

56

如何公开一个 List<T>,使其是只读的,但可以在类内私有地设置?

这种方式不起作用:

public List<string> myList {readonly get; private set; }
即使您执行以下操作:
public List<string> myList {get; private set; }

你仍然可以这样做:

myList.Add("TEST"); //This should not be allowed

我猜你可能有以下几种方案:

public List<string> myList {get{ return otherList;}}
private List<string> otherList {get;set;}
15个回答

105

我认为你正在混淆概念。

public List<string> myList {get; private set;}

myList已经是“只读”的了。也就是说,在这个类外面,不能用其他实例List<string>替换myList

但如果你需要一个只读的列表,就像“我不希望人们能够修改列表内容”,那么你需要暴露一个ReadOnlyCollection<string>。你可以通过以下方式实现:

private List<string> actualList = new List<string>();
public ReadOnlyCollection<string> myList
{
  get{ return actualList.AsReadOnly();}
}

请注意,在第一个代码片段中,其他人可以操作列表,但不能更改您拥有的列表。在第二个代码片段中,其他人将获得一个只读列表,他们无法修改它。


3
很好的观点。很多人都忽略了引用类型和将引用设置为只读的概念。+1 - Dustin Davis
4
更好的做法是将ReadOnlyCollection作为IList<string>。 - Joe
返回一个公共的System.Collections.ObjectModel.ReadOnlyCollection比在公共属性上使用“private set”更好,因为它可以提供编译时检查。这意味着当您在只读集合上使用Add()或RemoveAt()时,它会抛出编译错误,而“private set”则不会。 - MikeTeeVee
@MikeTeeVee private set; 不会导致 Add()RemoteAt() 抛出编译时错误 运行时错误。从类外部使用私有 setter 向 List 添加内容是完全合法的。这就是为什么 Philip 建议使用 ReadOnlyCollection 的第二种解决方案,以防止在需要此类行为的情况下对列表内容进行外部修改。 - Dan Bechard
1
@Dan 请再读一遍我的评论,因为这正是我所说的重点。如果不清楚,抱歉。 - MikeTeeVee
@MikeTeeVee 我重新阅读了一遍,但最后一句话似乎仍然暗示着您期望 private set 变体在运行时失败,因为 ReadOnlyCollection 执行了编译时检查。无论如何,答案已经非常清楚了,所以如果您和我都理解了,那就是最重要的。 (附言:我可以回复一个三年前的评论,并在 15 分钟内得到回应,这是 StackOverflow 用户中最好的特点之一)。 - Dan Bechard

11
如果您需要只读集合,请使用ReadOnlyCollection<T>,而不是List<T>
public ReadOnlyCollection<string> MyList { get; private set; }

11

我更喜欢使用IEnumerable

private readonly List<string> _list = new List<string>();

public IEnumerable<string> Values // Adding is not allowed - only iteration
{
   get { return _list; }
}

10
除了它存在缺陷,这是一个不错的方法:MyClass m = new MyClass(); List<string> x = m.Values as List<string>; x.Add("test"); foreach (string s in m.Values) { Console.WriteLine(s); } //通过进行强制转换,这允许我向其中添加值。 - Dustin Davis
6
好的,那是个技巧 :) 你也可以使用反射向内部集合添加元素。IEnumerable隐藏了MyClass客户端的实现细节。但你能确定我没有在内部使用LinkedList吗? - Sergey Berezovskiy
3
你可以通过反射来找到集合类型,这样我就能确信了。如果你要做,就要做正确的事情。当然,任何人都可以使用反射来“黑客”对象以添加项,但你无法控制那些操作。实践正确的技术并正确使用框架是你能做的唯一的事情。 - Dustin Davis
7
  1. 尝试将接口转换为某个类 - 这是接口的不正确使用,你应该制止这样做的人。
  2. yield语句有什么问题?
- Sergey Berezovskiy
3
如果API提供了ICollection<T>,那么将其转换为List<T>是一种hack方法。 - tster
显示剩余4条评论

10

返回一个实现了IList<>的ReadOnlyCollection

private List<string> myList;

public IList<string> MyList
{
  get{return myList.AsReadOnly();}
}

4
private List<string> my_list;

public ReadOnlyCollection<string> myList
{
    get { return my_list.AsReadOnly(); }
    private set { my_list = value; }
}

4

有一个叫做 ReadOnlyCollection<T> 的集合类型 - 这是你要找的吗?


4
你可以使用List的AsReadOnly()方法返回只读包装。

3
    private List<string> _items = new List<string>();         

    public ReadOnlyCollection<string> Items

    {

        get { return _items.AsReadOnly(); }

        private set { _items = value }

    }

3
这里有一种方法。
public class MyClass
    {
        private List<string> _myList;
        public ReadOnlyCollection<string> PublicReadOnlyList { get { return _myList.AsReadOnly(); } }

        public MyClass()
        {
            _myList = new List<string>();
            _myList.Add("tesT");
            _myList.Add("tesT1");
            _myList.Add("tesT2");

            //(_myList.AsReadOnly() as List<string>).Add("test 5");
        }

    }

1
嗯...我认为你可以使用PublicReadOnlyList,使用getter new ReadOnlyCollection<string>(_myList)。 - Sergey Berezovskiy

3
在.NET 4.5框架中,您只能公开IReadOnlyList接口。类似于:
private List<string> _mylist;
public IReadOnlyList<string> myList { get {return _myList;} }

或者,如果您想防止不必要的强制转换为IList

private List<string> _mylist;
public IReadOnlyList<string> myList { get {return new List<string>(_myList);} }

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