我花了好几个小时思考曝光名单成员的问题。在与我类似的问题中,乔恩·斯基特(Jon Skeet)给出了很好的答案。请随意看看。
ReadOnlyCollection 或 IEnumerable 用于公开成员集合? https://stackoverflow.com/questions/491375/readonlycollection-or-ienumerable-for-exposing-member-collections
我通常对公开列表非常偏执,尤其是当您正在开发 API 时。
我一直使用 IEnumerable 来公开列表,因为它非常安全,并且提供了很大的灵活性。让我在这里举个例子:
public class Activity
{
private readonly IList<WorkItem> workItems = new List<WorkItem>();
public string Name { get; set; }
public IEnumerable<WorkItem> WorkItems
{
get
{
return this.workItems;
}
}
public void AddWorkItem(WorkItem workItem)
{
this.workItems.Add(workItem);
}
}
任何针对 IEnumerable 进行编码的人在这里都是非常安全的。如果我后来决定使用有序列表或其他东西,它们的代码都不会中断,而且仍然很好。这样做的缺点是 IEnumerable 可以被强制转换回此类之外的列表。
因此,许多开发人员使用 ReadOnlyCollection 来公开成员。这是非常安全的,因为它永远不能被转换回列表。对我来说,我更喜欢 IEnumerable,因为它提供了更大的灵活性,如果我想要实现与列表不同的东西的话。
我想出了一个我更喜欢的新主意。使用 IReadOnlyCollection:
public class Activity
{
private readonly IList<WorkItem> workItems = new List<WorkItem>();
public string Name { get; set; }
public IReadOnlyCollection<WorkItem> WorkItems
{
get
{
return new ReadOnlyCollection<WorkItem>(this.workItems);
}
}
public void AddWorkItem(WorkItem workItem)
{
this.workItems.Add(workItem);
}
}
我觉得这保留了 IEnumerable 的一些灵活性,并且封装得相当好。
我发布这个问题是为了了解我的想法。与 IEnumerable 相比,您更喜欢此解决方案吗?您认为使用 ReadOnlyCollection 的具体返回值更好吗?这是一场相当激烈的辩论,我想尝试看看我们都能提出哪些优点/缺点。
EDIT
首先感谢大家对这里的讨论做出了如此多的贡献。我确实从每个人身上学到了很多东西,并真诚地感谢你们。
我正在添加一些额外的场景和信息。
IReadOnlyCollection 和 IEnumerable 有一些常见的陷阱。
考虑下面的例子:
public IReadOnlyCollection<WorkItem> WorkItems
{
get
{
return this.workItems;
}
}
即使接口是只读的,上面的示例也可以转换回列表并进行更改。尽管接口同名,但它并不能保证不变性。您需要提供一个不可变的解决方案,因此您应该返回一个新的 ReadOnlyCollection。通过创建一个新列表(本质上是一个副本),对象的状态是安全可靠的。
Richiban 在他的评论中说得最好:界面只保证某些东西可以做什么,而不保证它不能做什么。
请参阅下面的示例:
public IEnumerable<WorkItem> WorkItems
{
get
{
return new List<WorkItem>(this.workItems);
}
}
上面的内容可以被转换和改变,但你的对象仍然是不可变的。
另一个开箱即用的声明是集合类。考虑以下:
public class Bar : IEnumerable<string>
{
private List<string> foo;
public Bar()
{
this.foo = new List<string> { "123", "456" };
}
public IEnumerator<string> GetEnumerator()
{
return this.foo.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
上面的类可以具有按照您希望的方式改变 foo 的方法,但是您的对象永远不能转换为任何类型的列表并发生改变。
Carsten Führmann 对 IEnumerables 中的yield return 语句提出了一个精彩的观点。