ReadOnlyCollection或IEnumerable的用于曝光部件的集合?
-
20-08-2019 - |
题
是否有任何理由以暴露内部集合为ReadOnlyCollection而不是一个IEnumerable如果调用代码仅在收集迭代?
class Bar
{
private ICollection<Foo> foos;
// Which one is to be preferred?
public IEnumerable<Foo> Foos { ... }
public ReadOnlyCollection<Foo> Foos { ... }
}
// Calling code:
foreach (var f in bar.Foos)
DoSomething(f);
正如我看到它的IEnumerable是ReadOnlyCollection的接口的子集,它不允许用户修改的集合。因此,如果IEnumberable接口足够然后就是使用一个。是推理它还是我失去了一些东西?有道
感谢/埃里克
解决方案
更多现代溶液强>
除非你需要的内部集合是可变的,你可以使用 System.Collections.Immutable
一>包,改变你的字段类型是不可变的集合,然后暴露直接 - 假设Foo
本身是不可变的,当然
<强>更新的答案,以解决这个问题更直接地强>
是否有任何理由以暴露内部集合为ReadOnlyCollection而不是一个IEnumerable如果调用代码仅在收集迭代?
这取决于你有多少信任调用代码。如果您在完全控制已达到该会叫这个成员的一切,你的保证的,没有代码将永远使用:
ICollection<Foo> evil = (ICollection<Foo>) bar.Foos;
evil.Add(...);
话,当然,如果你只是直接返回集合无妨将完成。我一般尝试有点比,虽然更偏执。
同样地,当你说:如果你只的需要的IEnumerable<T>
,那么为什么扎自己什么都强。
<强>原始应答强>
如果您正在使用.NET 3.5,您可以避免复制的和的通过使用简单的调用跳过避免简单的转换:
public IEnumerable<Foo> Foos {
get { return foos.Skip(0); }
}
(有大量的包裹平凡的其他选项 - 约Skip
在选择/在哪里,有没有委托每次迭代无谓执行很好的事情。)
如果你不使用.NET 3.5,你可以写一个非常简单的包装,以做同样的事情:
public static IEnumerable<T> Wrapper<T>(IEnumerable<T> source)
{
foreach (T element in source)
{
yield return element;
}
}
其他提示
如果您只需要通过收集迭代:
foreach (Foo f in bar.Foos)
然后返回的的IEnumerable 强>就足够了。
如果你需要的物品随机访问:
Foo f = bar.Foos[17];
然后再包上的 ReadOnlyCollection 强>
如果你这样做,那么还有什么能阻止你的来电铸造了IEnumerable返回的ICollection,然后修改它。 ReadOnlyCollection消除这种可能性,尽管它仍然可以通过反射来访问底层写集合。如果集合较小,则一种安全,简便的方法来解决这个问题是返回一个复印件。
我避免使用ReadOnlyCollection尽可能,它实际上是比只用一个正常的列表相当慢。 看到这个例子:
List<int> intList = new List<int>();
//Use a ReadOnlyCollection around the List
System.Collections.ObjectModel.ReadOnlyCollection<int> mValue = new System.Collections.ObjectModel.ReadOnlyCollection<int>(intList);
for (int i = 0; i < 100000000; i++)
{
intList.Add(i);
}
long result = 0;
//Use normal foreach on the ReadOnlyCollection
TimeSpan lStart = new TimeSpan(System.DateTime.Now.Ticks);
foreach (int i in mValue)
result += i;
TimeSpan lEnd = new TimeSpan(System.DateTime.Now.Ticks);
MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
MessageBox.Show("Result: " + result.ToString());
//use <list>.ForEach
lStart = new TimeSpan(System.DateTime.Now.Ticks);
result = 0;
intList.ForEach(delegate(int i) { result += i; });
lEnd = new TimeSpan(System.DateTime.Now.Ticks);
MessageBox.Show("Speed(ms): " + (lEnd.TotalMilliseconds - lStart.TotalMilliseconds).ToString());
MessageBox.Show("Result: " + result.ToString());
有时你可能需要使用一个接口,也许是因为你想在单元测试过程嘲笑集合。请参阅我的博客条目通过使用添加自己的接口ReadonlyCollection的适配器。