只读的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个回答

2
private List<string> myList;

public string this[int i]
{
    get { return myList[i]; }
    set { myList[i] = value; }
}

1

为什么要使用List呢?听起来你真正想要暴露的是IEnumerable。

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

现在,类的用户可以读取项目,但不能更改列表。

1
有缺陷!你可以将其转换为List<>并添加值: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
4
是的,你可以做很多事情。当然,你也可以将myList设置为List<T>以外的其他东西,这样代码就会出现问题。我通常不担心因人们故意忽略封装而引起的问题。 - tster
4
将一个暴露的类型转换为另一个你知道其实现实际使用的类型,这就是破坏封装的定义。 - tster
2
@DustinDavis,消费者确实可以打破封装性。忽略API所提供的内容对您没有任何危害。这就像我向您出售一辆带有保修的汽车,而您去用巨大的甜甜圈更换轮胎一样。您可以这么做,但我不能保证结果。 - tster
2
刚刚碰到了一篇来自Bill Wagner的《Effective C# 50 Specific...》的内容:“在List<T>中公开IEnumerable<T>接口就是这种策略的一个例子。那些阴险的程序员可能会通过猜测实现接口的对象的类型并使用转换来打败它。但是,那些费尽心思制造错误的程序员也只能自食恶果。”第155页 - Adam Rackis
显示剩余2条评论

1
你也可以创建普通列表,但是通过一个类型为 IEnumerable 的属性公开它。
private List<int> _list = new List<int>();

public IEnumerable<int> publicCollection{
   get { return _list; }
}

有缺陷!你可以将其转换为List<>并添加值: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
1
事实上,即使_list.AsEnumerable()也可以转换为List,这让我感到惊讶。 - Adam Rackis
任何依赖将IEnumerable<T>强制转换为List<T>的人都是愚蠢的。 - Aluan Haddad

0
有点晚了,但还是要说:我不喜欢使用ReadOnlyCollection包装器,因为它仍然公开了所有修改集合的方法,当在运行时访问这些方法时,会抛出NotSupportedException。换句话说,它实现了IList接口,但在运行时违反了这个契约。
为了表达我真正公开的是一个不可变列表,我通常会使用自定义的IIndexable接口,它向IEnumerable添加了Length和索引器(在this CodeProject article中描述)。这是一个包装器,我认为一开始就应该这样做。

-1

我还没有看到这个选项被提到:

private List<string> myList;

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

这应该可以让你暴露一个只读列表。


这不太好。首先,您正在创建一个ReadOnlyCollection<T>来包装列表。这是次优的,因为ReadOnlyCollection<T>的实例实现了可变的IList<T>接口,并在运行时抛出异常,如果被修改。但是,您随后放弃了该集合,将其所有内容复制到新的List<T>中,这将允许修改,尽管不能修改原始列表。此外,此代码具有误导性,因为它公开了一个可变成员,该成员旨在是不可变的,但实际上是一个意外可变的副本。 - Aluan Haddad

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