C# 给定有限列表中的linq无限列表

C# 给定有限列表中的linq无限列表,c#,.net,linq,lazy-evaluation,infinite-sequence,C#,.net,Linq,Lazy Evaluation,Infinite Sequence,给定一个有限的元素列表,我如何创建一个(延迟计算,谢谢LINQ!)无限列表,它只在我的初始列表上不断迭代 如果初始列表是{1,2,3},我希望新列表返回{1,2,3,1,2,3,1,…}IEnumerable Infinite(此IEnumerable ienum) IEnumerable<T> Infinite(this IEnumerable<T> ienum) { List<T> list = ienum.ToList(); while

给定一个有限的元素列表,我如何创建一个(延迟计算,谢谢LINQ!)无限列表,它只在我的初始列表上不断迭代

如果初始列表是
{1,2,3}
,我希望新列表返回
{1,2,3,1,2,3,1,…}
IEnumerable Infinite(此IEnumerable ienum)
IEnumerable<T> Infinite(this IEnumerable<T> ienum)
{
    List<T> list = ienum.ToList();
    while (true)
       foreach(var t in list)
           yield return t;
}



foreach(int i in Enumerable.Range(1,3).Infinite())
      Console.WriteLine(i);
{ List=ienum.ToList(); while(true) foreach(列表中的变量t) 收益率t; } foreach(可枚举.Range(1,3).Infinite()中的int i) 控制台写入线(i);
以下是我最终是如何做到这一点的:

    public static IEnumerable<T> AdNauseam<T>(this IEnumerable<T> i_list)
    {
        using(var etor = i_list.GetEnumerator())
        {
            while(true)
            {
                while(etor.MoveNext())
                {
                    yield return etor.Current;
                }
                etor.Reset();
            }
        }
    }
结果是:

{1, 2, 3, 1, 2, 3, 1, 2, 3, 1}

yield return
是一个相当方便的操作符,尽管它实际上并不特别需要LINQ

IEnumerable<int> GetInfiniteSeries(IEnumerable<int> items) {
    while (true) {
       foreach (var item in items) { 
           yield return item;
       }
    }
}
IEnumerable GetInfiniteSeries(IEnumerable项){
while(true){
foreach(项目中的变量项目){
收益回报项目;
}
}
}

另一个选项是实施
IEnumerator

公共类InfiniteEnumerator:IEnumerator
{
私人IList_项目;
私有int_指数=-1;
公共无限计数器(IList项)
{
if(items==null)
{
抛出新的异常(“项目”);
}
_项目=项目;
}
公共电流
{
获取{return _items[_index];}
}
公共空间处置()
{
}
对象System.Collections.IEnumerator.Current
{
获取{return _items[_index];}
}
公共图书馆
{
如果(_items.Count==0)
{
返回false;
}
_指数=(_指数+1)%\u项目数;
返回true;
}
公共无效重置()
{
_指数=-1;
}
}

我想知道using()在这种情况下是否有用。using()是必要的-IEnumerator实现IDisposable。对于大多数列表,dispose可能没有多大作用,但如果枚举器正在执行一些新奇的操作,如从文件或数据库读取,您可能希望将其释放。考虑到“为COM互操作性提供了重置方法”,我想知道使用Reset是否是最佳选项。它不一定需要实施;相反,实现者可以简单地抛出NotSupportedException。“(源代码:)调用
ToList()
是必要的吗?+1:2问题:1。当
Infinite()
实现在逻辑上应该支持这一点时,这将不适用于
ienum.Infinite().Infinite()
。2.如果忽略第1点,则会出现性能问题:枚举数会不断被重新创建和释放。最好将其重新连接为for循环,当它点击
list.Count
时重置为
0
。另一种选择是依赖于
IEnumerator.Reset()
,但我认为这很危险,因为很多实现都不支持它。@Ani:你怎么知道有性能问题?在没有经验证据的情况下,您知道创建和销毁列表迭代器是用户应用程序中最慢的事情吗?或者您是否做了大量细致的分析工作,以确定此结构的分配和处置实际上是用户应用程序中最大的性能问题?如果是这样的话,我希望看到这些数据,以便我可以将其传递给BCL绩效团队,谢谢@Eric Lippert:我们在这里编写一个实用方法,所以我们不知道用户的应用程序!所以我想我们应该感谢用户尽可能地提高方法的性能,这与CLR团队尽可能快地制作列表迭代器的原因是一样的,他们不知道用户的应用程序中什么会变慢,是吗?当然,首先我没有任何“经验证据表明创建和销毁列表迭代器很慢”,所以我的建议可能是错误的。更正:应该是-BCL-team。比我的版本短很多:)我更喜欢这个实现,因为它更能描述您真正在做什么:无限期地枚举列表。它比无限的IEnumerable感觉更好,正如Ani所提到的,它避免了大脑爆炸,即
ienum.infinite().infinite()
IEnumerable<int> GetInfiniteSeries(IEnumerable<int> items) {
    while (true) {
       foreach (var item in items) { 
           yield return item;
       }
    }
}
  public class InfiniteEnumerator<T> : IEnumerator<T>
    {
        private IList<T> _items;
        private int _index = -1;

        public InfiniteEnumerator(IList<T> items)
        {
            if (items == null)
            {
                throw new ArgumentNullException("items");
            }
            _items = items;
        }

        public T Current
        {
            get { return _items[_index]; }
        }

        public void Dispose()
        {

        }

        object System.Collections.IEnumerator.Current
        {
            get { return _items[_index]; }
        }

        public bool MoveNext()
        {
            if (_items.Count == 0)
            {
                return false;
            }

            _index = (_index + 1) % _items.Count;
            return true;
        }

        public void Reset()
        {
            _index = -1;
        }
    }