- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我花了好几个小时思考公开列表成员的主题。在与我类似的问题中,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/
我正在为 MultiValueDictionary 创建一个扩展方法封装频繁ContainsKey检查,我想知道创建空 IReadOnlyCollection 的最佳方法是什么?。 到目前为止我使用的
我正在使用 selenium,我正在使用 FindElements 函数,所以我得到了一个实现 IReadOnlyCollection 接口(interface)的元素。我想遍历列表,但 IReadO
SO 上已经有几个类似的问题,但我发现没有一个真正涉及到这个特定主题,所以这里... 我的理解是,应该始终尝试通过具体类返回接口(interface)。不会深入探讨其背后的原因,已经有很多关于它的事情
我有IEnumerable并且需要传递给一个方法作为参数但是这个方法需要IReadOnlyCollection 是否可以转换IEnumerable至 IReadOnlyCollection ? 最佳答
为什么不同的集合(都实现了 IReadOnlyCollection 接口(interface))在试图将它们变成 IReadOnlyCollection 时被编译器以不同的方式处理。 ? IReadO
我想为 ICollection 和 IReadonlyCollection 接口(interface)编写扩展方法(例如 .IsEmpty()): public static bool IsEmpty
以下三种方法是否具有等效的垃圾回收行为? #1 有点牵强,但今天的 C# 编译器是否足够聪明,可以在每次调用方法时优化#2 中的对象创建?我特别不想在方法之外提升数组初始化。 public B
我的模型(使用 Entity Framework 生成,请记住我已针对示例对其进行了简化)如下所示: public class Person { public string Name { ge
我有一个 HashSet,我正试图将其转换为 IReadOnlyCollection,但出现错误: Cannot implicitly convert type 'System.Collections
我想模拟一个继承自 IReadOnlyCollection 的类。我写了一些示例代码来演示我的问题。三项断言中有两项有效。 当我将模拟的 IRemainingSteps 转换为列表或使用 LINQ 时
我正在迭代取自 HTML 的元素。 structureElement 似乎包含找到的元素,但是当我在其上运行 .FindElement() 时,它始终返回第一次迭代的值。这是为什么? 这是我的代码:
.NET 4.5 中新的只读接口(interface),例如 IReadOnlyCollection 和 IReadOnlyDictionary 非常有用,特别是因为它们已在常见的 BCL 类型上实现
我怀疑我对 System.Collection.Generic.IReadOnlyCollection 的理解语义并怀疑如何使用只读和不可变等概念进行设计。让我用 documentation 来描述我
我有一个私有(private)领域IList _elements并想要创建一个返回 IReadOnlyCollection 的属性. public IReadOnlyCollection BaseCl
当我有一个 ICollection 的变量时在 C# 中,我无法将它传递给需要 IReadOnlyCollection 的函数: public void Foo() { ICollection d
我刚刚发现 .NET Fx 现在有 3 个有用的接口(interface): IReadOnlyCollection IReadOnlyList IReadOnlyDictionary 我有点困惑为什
我正在处理对象的 IReadOnlyCollection。 现在我有点惊讶,因为我可以使用 linq 扩展方法 ElementAt()。但我无权访问 IndexOf()。 这对我来说看起来有点不合逻辑
这个问题看起来很简单。虽然documentation说是的: public sealed class KeyCollection : ICollection, IReadOnlyCollect
List工具 IReadOnlyCollection接口(interface)并提供AsReadOnly()返回 ReadOnlyCollection 的方法(它又实现了 IReadOnlyColle
我花了好几个小时思考公开列表成员的主题。在与我类似的问题中,Jon Skeet 给出了很好的答案。请随时查看。 ReadOnlyCollection or IEnumerable for exposi
我是一名优秀的程序员,十分优秀!