IEnumerable vs IReadonlyCollection vs ReadonlyCollection 用于公开列表成员

2024-04-13

我花了好几个小时思考曝光名单成员的问题。在与我类似的问题中,乔恩·斯基特(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 语句提出了一个精彩的观点。


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

When an IEnumerable<T>返回给调用者时,他们必须考虑返回的对象是“惰性流”的可能性,例如以“收益回报”构建的集合。也就是说,生产元素的性能损失IEnumerable<T>调用者可能必须为每次使用 IEnumerable 付费。 (生产力工具“Resharper”实际上指出这是代码味道。)

相比之下,一个IReadOnlyCollection<T>向调用者发出信号,表明不会有惰性求值。 (这Count财产,而不是 of IEnumerable<T>(继承自IReadOnlyCollection<T>所以它也有这个方法),表示不懒惰。事实上,似乎没有 IReadOnlyCollection 的惰性实现。)

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

IReadOnlyCollection 的作用not要做的就是防止调用者强制转换为其他集合类型。对于这种保护,人们必须使用class ReadOnlyCollection<T>.

综上所述,only thing IReadOnlyCollection<T>确实相对于IEnumerable<T>是添加一个Count财产,从而表明不涉及懒惰。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

IEnumerable vs IReadonlyCollection vs ReadonlyCollection 用于公开列表成员 的相关文章

  • 通过引用传递 [C++]、[Qt]

    我写了这样的东西 class Storage public Storage QString key const int value const void add item QString int private QMap
  • 机器Epsilon精度差异

    我正在尝试计算 C 中双精度数和浮点数的机器 epsilon 值 作为学校作业的一部分 我在 Windows 7 64 位中使用 Cygwin 代码如下 include
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • 为什么 GCC 不允许我创建“内联静态 std::stringstream”?

    我将直接前往 MCVE include
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • 枚举扩展方法

    在vs2008中 是否可以编写适用于任何枚举的扩展方法 我知道您可以针对特定枚举编写扩展方法 但我希望能够使用单个扩展方法对每个枚举进行处理 这可能吗 是的 只需针对基础进行编码Enum类型 例如 public static void So
  • 需要帮助优化算法 - 两百万以下所有素数的总和

    我正在尝试做一个欧拉计划 http projecteuler net问题 我正在寻找 2 000 000 以下所有素数的总和 这就是我所拥有的 int main int argc char argv unsigned long int su
  • 人脸 API DetectAsync 错误

    我想创建一个简单的程序来使用 Microsoft Azure Face API 和 Visual Studio 2015 检测人脸 遵循 https social technet microsoft com wiki contents ar
  • ASP.NET Core 3.1登录后如何获取用户信息

    我试图在登录 ASP NET Core 3 1 后获取用户信息 如姓名 电子邮件 id 等信息 这是我在登录操作中的代码 var claims new List
  • mvc显示模板中当前项目的索引

    我有一个带有显示模板的 mvc 页面 如何获取显示模板中呈现的当前项目的索引 它在名称属性中产生正确的可绑定结果
  • x:将 ViewModel 方法绑定到 DataTemplate 内的事件

    我基本上问同样的问题这个人 https stackoverflow com questions 10752448 binding to viewmodels property from a template 但在较新的背景下x Bind V
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • 引用的程序集自动由 Visual Studio 替换

    我有 2 个项目 一个可移植类库和一个常规单元测试项目 在可移植类库中 我使用 NuGet 来引用 Microsoft BCL 可移植包 它附带 2 个程序集 System Threading Tasks dll and System Ru
  • 为什么使用小于 32 位的整数?

    我总是喜欢使用最小尺寸的变量 这样效果就很好 但是如果我使用短字节整数而不是整数 并且内存是 32 位字可寻址 这真的会给我带来好处吗 编译器是否会做一些事情来增强内存使用 对于局部变量 它可能没有多大意义 但是在具有数千甚至数百万项的结构
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • DotNetZip:如何提取文件,但忽略zip文件中的路径?

    尝试将文件提取到给定文件夹 忽略 zip 文件中的路径 但似乎没有办法 考虑到其中实现的所有其他好东西 这似乎是一个相当基本的要求 我缺少什么 代码是 using Ionic Zip ZipFile zf Ionic Zip ZipFile
  • MySQL Connector C/C API - 使用特殊字符进行查询

    我是一个 C 程序 我有一个接受域名参数的函数 void db domains query char name 使用 mysql query 我测试数据库中是否存在域名 如果不是这种情况 我插入新域名 char query 400 spri
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前

随机推荐

  • 在jquery中创建循环背景动画

    我想要的是 当页面加载时 背景颜色为红色 10 秒后 bgColor 变为绿色 并带有淡入淡出动画 再过 10 秒后 它会变成橙色 然后又变成红色 依此类推 有人可以帮忙吗 Use 设置间隔 http www w3schools com j
  • Firestore - 如何在数据库中自动减少整数值?

    Firestore 最近推出了一项新功能 可以自动递增和递减整数值 我可以使用增加整数值 例如 FieldValue increment 50 但如何减量呢 我尝试使用FieldValue decrement 50 但FieldValue中
  • 在 C++ 中旋转图像而不使用 OpenCV 函数

    描述 我正在尝试在不使用 C 中的 OpenCV 函数的情况下旋转图像 旋转中心不必是图像的中心 它可能是不同的点 偏离图像中心 到目前为止 我遵循各种来源进行图像插值 我知道source https stackoverflow com a
  • 检测节点的系统路径上是否存在可执行文件

    Question 有没有一种简单的方法可以使用节点判断系统可执行文件在系统路径上是否可用 例如 如果用户安装了 python usr bin python and usr bin is in PATH我如何在 Node 中检测到这一点 相反
  • 在 Filter 内执行并呈现 JSP

    我有一个包含页面顶部内容的 JSP 我们将其称为 header jsp 出于性能原因 我想在构建页面的其余部分之前呈现此 JSP 并将其刷新给用户 看here http developer yahoo com performance rul
  • 元素不可点击错误 Ruby / Watir

    在我的测试中 我尝试访问 etsy com 进行搜索 单击结果 然后将商品添加到我的购物车 我可以做所有事情 直到我尝试单击 添加到购物车 按钮 下面的代码实际上在 IRB 中工作 所以我知道我的定位器是可靠的 但是当我运行测试时 我得到一
  • 在 Elasticsearch 中过滤折叠结果

    我有一个弹性搜索索引 其中包含表示给定时间点实体的文档 当实体更改状态时 会创建带有时间戳的新文档 当我需要获取所有实体的当前状态时 我可以执行以下操作 GET https 127 0 0 1 9200 myindex search col
  • .NET RIA 服务与 MVC 风格存储库?

    我有一个包含多个项目的解决方案 包括两个共享位于外部程序集中 也在同一解决方案中 的存储库和模型的 asp net mvc 项目 本质上 Core 存储库 楷模 国内 Web 基本MVC站点 引用核心项目 国际网 基本MVC站点 引用核心项
  • 使用 NativeMessaging 进行边缘扩展

    我有一个具有本机消息传递支持的边缘扩展 它与系统中运行的 uwp 应用程序进行通信 是否必须将扩展打包到 uwp 应用程序的 appx 文件夹中 如果没有 那么我们如何将扩展上传到边缘扩展存储中 我遵循 secureInput 示例 htt
  • 快速向 SQL Server 插入 200 万行

    我必须从文本文件中插入大约 200 万行 通过插入 我必须创建一些主表 将如此大的数据集插入 SQL Server 的最佳且快速的方法是什么 我认为最好读取 DataSet 中文本文件的数据 试用SQL批量复制 从 C 应用程序批量插入 S
  • GSON反序列化:如何知道对象?

    我尝试使用 gson 库来反序列化发送给我的对象流 在我见过的所有示例中 当调用 fromJson 方法时 我们已经知道我们期望拥有什么类型的对象 就我而言 我收到不同对象的流 我想知道在反序列化对象之前了解对象类的最佳方法 A B B1
  • Android 自定义通知布局与 RemoteViews

    我正在尝试使用此为我的 Android 应用程序创建自定义通知post https stackoverflow com questions 18367631 change notification layout 我偶然发现了一个我在过去 2
  • 使用 LIMIT 时获取总行数? [复制]

    这个问题在这里已经有答案了 可能的重复 使用 offset limit 查找 mySQL 查询中的结果总数 https stackoverflow com questions 5928611 find total number of res
  • JAX-RS自定义ExceptionMapper不拦截RuntimeException

    我想包裹底层RuntimeExceptions自定义 json 格式 使得 servlet 容器不会将堆栈跟踪转储到客户端 我关注这个问题 使用 XML 或 JSON 的 JAX RS Jersey 自定义异常 https stackove
  • 强制 MySQL 在连接上使用两个索引

    我试图强制 MySQL 使用两个索引 我正在加入一个表 我想利用两个索引之间的交叉 具体术语是 Using intersect 这里是 MySQL 文档的链接 http dev mysql com doc refman 5 0 en ind
  • Xamarin 表单:从 Xamarin Forms 应用程序启动 IOS 应用程序时出现问题(输入 URL 无效)

    我正在尝试从我的 xamarin forms ios 应用程序打开一个 ios 应用程序 我已经提到过this https stackoverflow com questions 43944283 launch another ios ap
  • 使用 NuGet 安装 NHibernate 3.2

    我是 NHibernate 的新手 一直在尝试使用 NuGet 来启动和运行它 Fluent NHibernate 和 NProf 读完这篇文章后 http gurustop net blog 2011 03 13 nhibernate 3
  • SVGforeignObject 无法在任何浏览器上显示,为什么?

    我在 SVG 元素中有一个foreignObject 所有其他元素都会显示 但foreignObject 及其内容是不可见的 在 Chrome Firefox 和 Edge 中进行了测试 结果均相同 这是代码
  • 尝试使用 PHP 通过 HMAC-SHA1 进行数字签名

    我正在尝试设置一些 Google Maps Premier API 操作 为此 我需要对我的网址进行签名以进行身份 验证 如果您查看签名示例 可以看到一些 Python C 和 Java 代码向您展示如何通过 HMAC SHA1 进行签名
  • IEnumerable vs IReadonlyCollection vs ReadonlyCollection 用于公开列表成员

    我花了好几个小时思考曝光名单成员的问题 在与我类似的问题中 乔恩 斯基特 Jon Skeet 给出了很好的答案 请随意看看 ReadOnlyCollection 或 IEnumerable 用于公开成员集合 https stackoverf