c# - 用于公开列表成员的 IEnumerable vs IReadonlyCollection vs ReadonlyCollection

标签 c# .net list ienumerable encapsulation

我花了好几个小时思考公开列表成员的主题。在与我类似的问题中,Jon Skeet 给出了很好的答案。请随时查看。

ReadOnlyCollection or IEnumerable for exposing member collections?

我通常对公开列表非常偏执,尤其是当您正在开发 API 时。

我一直使用 IEnumerable 来公开列表,因为它非常安全,而且它提供了很大的灵 active 。让我在这里举个例子:

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,因为它提供了更多的灵 active ,如果我想要实现不同于列表的东西的话。

我想出了一个我更喜欢的新点子。使用 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 的一些灵 active 并且封装得很好。

我发布这个问题是为了获得一些关于我的想法的意见。与 IEnumerable 相比,您更喜欢此解决方案吗?您认为使用 ReadOnlyCollection 的具体返回值更好吗?这是一场相当激烈的辩论,我想尝试看看我们都可以提出哪些优点/缺点。

预先感谢您的输入。

编辑

首先感谢大家对这里的讨论做出如此多的贡献。我当然从每个人那里学到了很多东西,并真诚地感谢你们。

我正在添加一些额外的场景和信息。

IReadOnlyCollection 和 IEnumerable 有一些常见的缺陷。

考虑下面的例子:

public IReadOnlyCollection<WorkItem> WorkItems
{
    get
    {
        return this.workItems;
    }
}

上面的例子可以转换回一个列表并改变,即使接口(interface)是只读的。接口(interface),尽管它是同名的,但不能保证不变性。由您提供不可变的解决方案,因此您应该返回一个新的 ReadOnlyCollection。通过创建一个新列表(本质上是一个副本),您的对象的状态是安全的。

Richiban 在他的评论中说得最好:接口(interface)只保证某些东西可以做什么,而不保证它不能做什么。

示例如下:

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 语句提出了一个绝妙的观点。

再次感谢大家。

最佳答案

到目前为止,答案中似乎缺少一个重要方面:

IEnumerable<T>返回给调用者,他们必须考虑返回的对象是“惰性流”的可能性,例如一个以“ yield 返回”为基础的收藏。也就是说,生成 IEnumerable<T> 的元素的性能损失调用者可能必须为每次使用 IEnumerable 付费。 (生产力工具“Resharper”实际上指出这是一种代码味道。)

相比之下,IReadOnlyCollection<T>向调用者发出信号,表明不会进行惰性求值。 (Count 属性,与 Count IEnumerable<T> extension method 相反(它由 IReadOnlyCollection<T> 继承,所以它也有方法),表示非懒惰。事实上似乎有没有 IReadOnlyCollection 的惰性实现。)

这对输入参数也有效,因为请求 IReadOnlyCollection<T>而不是 IEnumerable<T>表明该方法需要对集合进行多次迭代。当然,该方法可以从 IEnumerable<T> 创建自己的列表并对其进行迭代,但由于调用者手头可能已经有一个加载的集合,因此尽可能利用它是有意义的。如果调用者只有一个 IEnumerable<T>在手头,他只需要添加 .ToArray().ToList()到参数。

IReadOnlyCollection 做的是防止调用者强制转换为其他集合类型。对于这样的保护,必须使用 ReadOnlyCollection<T> .

总而言之,唯一的东西IReadOnlyCollection<T>相对于 IEnumerable<T>是加一个Count属性,从而表明不涉及懒惰。

关于c# - 用于公开列表成员的 IEnumerable vs IReadonlyCollection vs ReadonlyCollection,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24880268/

相关文章:

java - 构造一个新方法 printList,它显示 LinkedList<String> 的内容

python - 将字符串列表转换为元组列表

c# - sslStream.AuthenticateAsClient 适用于 Windows 7 但在 Windows 8 上失败

c# - 检查用户输入的时间格式是否正确

javascript - 使用 ASP.NET MVC JavaScript 的斐波那契算法

c# - HttpWebResponse 输出流不关闭

c# - HashSet 中的剩余值进入辅助输出

c# - LINQ 到 SQL : Implementing the IN clause

c# - 如何统计datagridview列中某一列的特定值

Java 选择列表元素