必须.NET的IList的是有限的? 假设我写一个类FibonacciList实现IList<BigInteger>
- 该物业项目[N]返回第n个Fibonacci数。
- IsReadOnly返回true属性。
- 该方法的IndexOf和包含我们就可以实现轻松不够,因为Fibonacci序列增加-测试如果数量m是斐波那契数,我们只需要计算斐波那契数的有限序列不小于m。
- 该方法GetEnumerator()做正确的事
现在,我们已经实现了预期的只读ILists除外伯爵的所有方法()。
这是很酷的,或IList的滥用?
斐波那契数得到不切实际的大快速(因此IList<BigInteger>
以上)。 有界无限序列可能是更明智的,它可以实现IList<long>
或IList<double>
。
附录II:斐波那契序列可能是一个坏榜样,因为计算遥远值是昂贵的 - 找到第n个值一个需要计算所有以前的值。 因此,随着Mošmondor说,一个还不如让一个IEnumerable和使用.ElementAt
。 然而存在的其它序列,其中可以快速计算遥远值,而不计算以前的值 。 (令人惊讶的是圆周率的数字是这样的序列 )。 这些序列是更多的“listy”,他们真正支持随机访问。
编辑:没有人反驳了无限IEnumerables。 他们如何处理COUNT()?
大多数开发, IList
和ICollection
暗示,你有一个预评估,内存中的集合一起工作。 随着IList
明确,有固定时间的隐性契约Add
*和索引操作。 这就是为什么LinkedList<T>
未实现IList<T>
我会考虑FibonacciList是违反本合同默示的。
注意从以下段落最近的MSDN杂志文章讨论添加只读集合接口到.NET 4.5的原因:
IEnumerable<T>
就足够了该处理的类型的集合,但有时你需要更多的力量比它提供了大部分的场景:
- 物化:
IEnumerable<T>
不允许你表达集合是否已经可用(“物化”),还是它的计算每次迭代它的时候(例如,如果它代表LINQ查询)。 当一个算法需要在采集多次迭代,这可能会导致性能降低,如果计算所述序列是昂贵的; 当被再次产生对后续行程的对象,也可能会导致因身份不匹配的微妙的错误。
正如其他人所指出的,还有,你会回到什么问题.Count
。
这是完全正常使用IEnumerable
或IQueryable
在数据的收集等,因为有一个期望,这些类型可以懒洋洋地评估。
关于编辑1: .Count()
不被执行IEnumerable<T>
接口:它是一个扩展方法 。 因此,开发人员需要的期望,它可以采取任何的时间,他们需要避免调用它,他们实际上并不需要知道的项目数的情况。 例如,如果你只是想知道是否一个IEnumerable<T>
有任何物品,最好使用.Any()
如果你知道,有要处理项目的最大数量,你可以使用.Take()
如果收藏有超过int.MaxValue
在它的项目, .Count()
会遇到的操作溢出。 因此,有一些解决方法,可以帮助减少与无限的序列相关的危险。 显然,如果程序员没有考虑这些可能性考虑在内,但仍可能会造成问题,但。
关于编辑2:如果您正计划实施的序列的方式,分度是恒定的时候,也很方便地解决了我的主要观点。 Sixlettervariables的回答仍然是成立的,但。
*显然还有更多这样的: Add
只是预期,如果工作IList.IsFixedSize
返回false
。 修改只可能是IsReadOnly
返回false等IList
是摆在首位的难深思熟虑的接口:这可能最终在.NET 4.5中引入的只读集合接口予以纠正的事实。
更新
已经给这一些额外的想法,我是来个人认为IEnumerable<>
不应为无限任。 除了像物化方法.ToList()
,LINQ具有像几个非流操作.OrderBy()
必须消耗整个IEnumerable<>
第一个结果之前可以被返回。 由于如此多的方法假定IEnumerable<>
s为安全的全部遍历,这将是一个违反里氏替换原则的,以产生IEnumerable<>
是固有的不安全无限期地遍历。
如果你发现你的应用程序往往需要斐波那契序列IEnumerables的部分,我建议有类似签名产生的方法Enumerable.Range(int, int)
它允许用户定义一个起始和结束索引。
如果你想踏上哎呀,飕飕的项目,你可以想见,开发基于斐波那契- IQueryable<>
提供商,即用户可以使用LINQ查询语法的一个有限的子集,就像这样:
// LINQ to Fibonacci!
var fibQuery = from n in Fibonacci.Numbers // (returns an IQueryable<>)
where n.Index > 5 && n.Value < 20000
select n.Value;
var fibCount = fibQuery.Count();
var fibList = fibQuery.ToList();
由于您的查询提供必须评估的动力where
的条款作为lambda表达式,你可以有足够的控制,以实现Count
方法和.GetEnumerator()
的方式,以确保查询足以限制产生真正的答案,或只要方法称为抛出异常。
但这种恶臭的是聪明,而且很可能是任何现实生活中的软件非常糟糕的主意。
我会想象,一个符合标准的实现必须是有限的,否则你会返回ICollection<T>.Count
?
/// <summary>
/// Gets the number of elements contained in the <see cref="ICollection{T}" />.
/// </summary>
int Count { get; }
另一个考虑因素是CopyTo
,它其正常过载情况下绝不会在斐波那契的情况下停止。
这意味着是一个适当的实施斐波纳契数列的将是简单地IEnumerable<int>
(使用发电机模式)。 (AB)使用的IList<T>
只会导致问题。
在你的情况,我宁愿“侵犯” IEnumerable
,并有我的路yield return
。
:)
无限集很可能最好地实现为IEnumerable<T>
不是一个IList<T>
你也可以利用的yield return
实现的时候,像这样(忽略溢出问题等)语法:
public IEnumerable<long> Fib()
{
yield return 1;
yield return 1;
long l1 = 1;
long l2 = 1;
while (true)
{
long t = l1;
l1 = l2;
l2 = t + l1;
yield return l2;
}
}
作为@CodeInChaos在评论中指出,IList中的项目酒店签名
T this[ int index ] { get; set; }
我们看到ILists由整数索引,因此它们的长度由Int32.MaxValue界。 指数越大的内容将无法访问。 写的问题时,这发生在我身上,但我离开它,因为这个问题是考虑另有乐趣。
编辑
有过了一天,以反映我的答案,并在@ StriplingWarrior的评论的光。 我担心我必须做出一个逆转。 我开始了这一点,昨晚尝试,现在我不知道我会真的放弃失去IList
?
我认为它会更明智仅实现IEnumerable
,并声明局部Count()
抛出的方法NotSupportedException
的方法,防止跑,直到枚举OutOfMemoryException
发生。 我仍然会添加一个IndexOf
和Contains
的方法和Item
索引属性暴露性能更高的替代品像比奈的公式,但,我就可以自由更改这些成员的签名可能使用扩展数据类型,甚至System.Numerics.BigInteger
。
如果我实现多个系列中,我将宣布一个ISeries
为这些成员接口。 谁知道的,也许这样的财产以后最终将框架的一部分。
我不同意这似乎是一种共识。 虽然IList
有很多成员,不能为一个无穷级数来实现它确实有一个IsReadOnly
成员。 这似乎可以接受的,在一定的情况下ReadOnlyCollection<>
以实现与所涉及的大多数成员NotSupportedException
。 根据这一先例,我不明白为什么这应该是不可接受的,如果它在功能上的一些其它增益的副作用。
在这个特定的Fibbonaci系列的情况下,有既定algortihms,见这里和这里 ,为短路正常cumalitive列举的办法,我认为会产生siginifcant性能优势。 通过揭露这些好处IList
似乎有必要给我。
理想的情况下,净会支持一些其他的,更合适的超类接口,稍微接近于IEnumerable<>
但是,直到在将来的某个版本的到来,这一定是一个明智的做法。
我工作的一个实现IList<BigInteger>
说明
总结什么我迄今为止看到:
你能完成5出6,扔一个NotSupportedException
的Count()
我会说,这可能是不够好,去了,但是作为servy
指出,索引为任何非计算和缓存数量令人难以置信的效率低下。
在这种情况下,我要说的是适合你的计算连续流IEnumerable的唯一合同。
你有另一种选择是创造的东西,看起来很像一个IList
,但实际上不是。