如何允许迭代私有集合但不修改?

izb*_*izb 6 c# iterator design-patterns

如果我有以下班级成员:

private List<object> obs;
Run Code Online (Sandbox Code Playgroud)

我希望允许遍历此列表作为类接口的一部分,我该怎么做?

公开它是行不通的,因为我不想直接修改列表.

cas*_*One 13

你会把它暴露为IEnumerable<T>,但不只是直接返回它:

public IEnumerable<object> Objects { get { return obs.Select(o => o); } }
Run Code Online (Sandbox Code Playgroud)

既然您表示只想要遍历列表,那么这就是您所需要的.

人们可能会试图List<object>直接返回IEnumerable<T>,但这是不正确的,因为人们可以IEnumerable<T>在运行时轻松检查,确定它是一个List<T>并将其转换为此类并改变内容.

但是,通过使用return obs.Select(o => o);最终返回迭代器List<object>,而不是直接引用List<object>自身.

根据C#语言规范的第7.15.2.5节,有些人可能会认为这符合"退化表达式".然而,Eric Lippert详细介绍了为什么这个投影没有被优化掉.

此外,人们建议使用AsEnumerable扩展方法.这是不正确的,因为维护了原始列表的引用标识.从文档的备注部分:

AsEnumerable<TSource>(IEnumerable<TSource>)除了将源代码的编译时类型从实现的类型更改IEnumerable<T>IEnumerable<T>自身之外,该方法没有任何效果.

换句话说,它所做的只是将源参数强制转换为IEnumerable<T>,这无助于保护参照完整性,返回原始引用并可以转换回List<T>并用于改变列表.

  • 请注意,如果出现在代码中,我们绝不会优化掉*select*select.在将查询表达式转换为方法调用时,我们将优化掉对Select的隐式调用.如果你说"from x in y where b select x",则没有Select call,只有Where.但是如果你说"从x中选择x",那么我们不只是生成"y",我们会在其上生成一个select调用以确保结果的不变性. (2认同)

bru*_*nde 11

您可以使用ReadOnlyCollection或复制List它并将其返回(考虑到复制操作的性能损失).你也可以使用List<T>.AsReadOnly.