【发布时间】:2011-01-18 20:38:52
【问题描述】:
如果我有以下班级成员:
private List<object> obs;
并且我想允许将此列表作为类接口的一部分进行遍历,我该怎么做?
将其公开是行不通的,因为我不想让列表被直接修改。
【问题讨论】:
标签: c# design-patterns iterator
如果我有以下班级成员:
private List<object> obs;
并且我想允许将此列表作为类接口的一部分进行遍历,我该怎么做?
将其公开是行不通的,因为我不想让列表被直接修改。
【问题讨论】:
标签: c# design-patterns iterator
只需返回 IReadOnlyCollection。
private List<object> obs;
IReadOnlyCollection<object> GetObjects()
{
return obs;
}
【讨论】:
您可以通过两种方式做到这一点:
通过将列表转换为只读集合:
new System.Collections.ObjectModel.ReadOnlyCollection<object>(this.obs)
或者通过返回项目的 IEnumerable:
this.obs.AsEnumerable()
【讨论】:
您可以将其公开为IEnumerable<T>,但不只是直接返回:
public IEnumerable<object> Objects { get { return obs.Select(o => o); } }
由于您表示您只想要列表的遍历,这就是您所需要的。
人们可能想直接将List<object> 作为IEnumerable<T> 返回,但这是不正确的,因为人们可以在运行时轻松检查IEnumerable<T>,确定它是List<T> 并将其转换为这样的并改变内容。
但是,通过使用 return obs.Select(o => o);,您最终会在 List<object> 上返回一个迭代器,而不是对 List<object> 本身的直接引用。
根据 C# 语言规范的第 7.15.2.5 节,有些人可能认为这属于“退化表达式”。但是,Eric Lippert goes into detail as to why this projection isn't optimized away。
此外,人们建议使用AsEnumerable extension method。这是不正确的,因为原始列表的引用标识被保留。从文档的备注部分:
AsEnumerable<TSource>(IEnumerable<TSource>)方法除了将源的编译时类型从实现IEnumerable<T>的类型更改为IEnumerable<T>本身之外没有任何作用。
换句话说,它所做的只是将源参数转换为IEnumerable<T>,这无助于保护引用完整性,原始引用被返回并且可以被转换回List<T>并用于改变列表.
【讨论】:
在您的接口中添加以下方法签名: public IEnumerable TraverseTheList()
实现如下:
public IEnumerable<object> TraverseTheList()
{
foreach( object item in obj)
{
yield return item;
}
}
这将允许您执行以下操作:
foreach(object item in Something.TraverseTheList())
{
// do something to the item
}
yield 返回告诉编译器为你构建一个枚举器。
【讨论】:
这已经说过了,但我认为没有任何答案是非常清楚的。
最简单的方法是简单地返回一个 ReadOnlyCollection
private List<object> objs;
public ReadOnlyCollection<object> Objs {
get {
return objs.AsReadOnly();
}
}
这样做的缺点是,如果您想稍后更改您的实现,那么一些调用者可能已经依赖于集合提供随机访问这一事实。所以更安全的定义是只公开一个 IEnumerable
public IEnumerable<object> Objs {
get {
return objs.AsReadOnly();
}
}
请注意,您不必调用 AsReadOnly() 来编译此代码。但如果你不这样做,调用者我只是将返回值转换回列表并修改你的列表。
// Bad caller code
var objs = YourClass.Objs;
var list = objs as List<object>;
list.Add(new object); // They have just modified your list.
同样的,这个解决方案也存在潜在问题
public IEnumerable<object> Objs {
get {
return objs.AsEnumerable();
}
}
因此,我绝对建议您在列表中调用 AsReadOnly(),并返回该值。
【讨论】:
您可以使用ReadOnlyCollection 或复制List 并将其返回(考虑到复制操作的性能损失)。你也可以使用List<T>.AsReadOnly。
【讨论】:
【讨论】:
公开ReadOnlyCollection<T>
【讨论】:
ReadOnlyCollection<T>。
您是否考虑过从 System.Collections.ReadOnlyCollectionBase 派生一个类?
【讨论】: