【发布时间】:2014-09-12 20:29:35
【问题描述】:
我花了好几个小时来思考暴露列表成员的问题。在与我类似的问题中,Jon Skeet 给出了一个很好的答案。请随意看看。
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 的具体返回值更好吗?这是一场激烈的辩论,我想尝试看看我们都可以提出哪些优点/缺点。
提前感谢您的意见。
编辑
首先感谢大家为这里的讨论做出了如此多的贡献。我当然从每一个人身上学到了很多东西,并真诚地感谢你。
我正在添加一些额外的场景和信息。
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 语句提出了精彩的观点。
再次感谢大家。
【问题讨论】:
-
嗯,我认为您应该阅读很棒的 Jon Skeet anwser:stackoverflow.com/questions/491375/… [可能重复]
-
如果您从顶部阅读问题,您会发现我在我的问题中引用了 John Skeet 的回答作为类似问题。 :) 虽然答案写得很好,但我相信有一些话题没有完全涉及到这个
-
没问题。很容易错过。 :)
-
我无法理解 IReadOnlyCollection 如何永远无法转换为 List,我们不能这样做 WorkItems.AsEnumerable().ToList(); ??
-
嗨,阿南思。好问题。自从我想到这个问题以来已经有一段时间了,从那以后我学到了很多东西。使用 WorkItmes.AsEnumerable().ToList() 将创建列表的副本,并且您的类不会发生变异。在上述情况下,由于返回了一个新的只读集合,您将永远无法将其转换回列表。您的演员表将为空 话虽如此,IReadOnlyCollection 存在一个常见的陷阱。如果您在 get 中返回列表,则有人可以将其转换为列表并改变对象的状态。为了清楚起见,我将更新问题
标签: c# .net list ienumerable encapsulation