必须IList的是有限的?(Must IList be finite?)

2019-07-30 08:34发布

必须.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()?

Answer 1:

大多数开发, IListICollection暗示,你有一个预评估,内存中的集合一起工作。 随着IList明确,有固定时间的隐性契约Add *和索引操作。 这就是为什么LinkedList<T>未实现IList<T> 我会考虑FibonacciList是违反本合同默示的。

注意从以下段落最近的MSDN杂志文章讨论添加只读集合接口到.NET 4.5的原因:

IEnumerable<T>就足够了该处理的类型的集合,但有时你需要更多的力量比它提供了大部分的场景:

  • 物化: IEnumerable<T>不允许你表达集合是否已经可用(“物化”),还是它的计算每次迭代它的时候(例如,如果它代表LINQ查询)。 当一个算法需要在采集多次迭代,这可能会导致性能降低,如果计算所述序列是昂贵的; 当被再次产生对后续行程的对象,也可能会导致因身份不匹配的微妙的错误。

正如其他人所指出的,还有,你会回到什么问题.Count

这是完全正常使用IEnumerableIQueryable在数据的收集等,因为有一个期望,这些类型可以懒洋洋地评估。

关于编辑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()的方式,以确保查询足以限制产生真正的答案,或只要方法称为抛出异常。

但这种恶臭的是聪明,而且很可能是任何现实生活中的软件非常糟糕的主意。



Answer 2:

我会想象,一个符合标准的实现必须是有限的,否则你会返回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>只会导致问题。



Answer 3:

在你的情况,我宁愿“侵犯” IEnumerable ,并有我的路yield return

:)



Answer 4:

无限集很可能最好地实现为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;
    }
}


Answer 5:

作为@CodeInChaos在评论中指出,IList中的项目酒店签名

T this[ int index ] { get; set; }

我们看到ILists由整数索引,因此它们的长度由Int32.MaxValue界。 指数越大的内容将无法访问。 写的问题时,这发生在我身上,但我离开它,因为这个问题是考虑另有乐趣。



Answer 6:

编辑

有过了一天,以反映我的答案,并在@ StriplingWarrior的评论的光。 我担心我必须做出一个逆转。 我开始了这一点,昨晚尝试,现在我不知道我会真的放弃失去IList

我认为它会更明智仅实现IEnumerable ,并声明局部Count()抛出的方法NotSupportedException的方法,防止跑,直到枚举OutOfMemoryException发生。 我仍然会添加一个IndexOfContains的方法和Item索引属性暴露性能更高的替代品像比奈的公式,但,我就可以自由更改这些成员的签名可能使用扩展数据类型,甚至System.Numerics.BigInteger

如果我实现多个系列中,我将宣布一个ISeries为这些成员接口。 谁知道的,也许这样的财产以后最终将框架的一部分。


我不同意这似乎是一种共识。 虽然IList有很多成员,不能为一个无穷级数来实现它确实有一个IsReadOnly成员。 这似乎可以接受的,在一定的情况下ReadOnlyCollection<>以实现与所涉及的大多数成员NotSupportedException 。 根据这一先例,我不明白为什么这应该是不可接受的,如果它在功能上的一些其它增益的副作用。

在这个特定的Fibbonaci系列的情况下,有既定algortihms,见这里和这里 ,为短路正常cumalitive列举的办法,我认为会产生siginifcant性能优势。 通过揭露这些好处IList似乎有必要给我。

理想的情况下,净会支持一些其他的,更合适的超类接口,稍微接近于IEnumerable<>但是,直到在将来的某个版本的到来,这一定是一个明智的做法。


我工作的一个实现IList<BigInteger>说明



Answer 7:

总结什么我迄今为止看到:

你能完成5出6,扔一个NotSupportedExceptionCount()

我会说,这可能是不够好,去了,但是作为servy指出,索引为任何非计算和缓存数量令人难以置信的效率低下。

在这种情况下,我要说的是适合你的计算连续流IEnumerable的唯一合同。

你有另一种选择是创造的东西,看起来很像一个IList ,但实际上不是。



文章来源: Must IList be finite?