如果我正走过一个 IEnumerable<T>,有什么办法可以得到新的 IEnumerable<T> 表示当前项之后的剩余项。

例如我想写一个扩展方法 IEnumerator<T>.Remaining():

IEnumerable<int> sequence = ...
IEnumerator<int> enumerator = sequence.GetEnumerator();

if (enumerator.MoveNext() && enumerator.MoveNext()) {
    IEnumerable<int> rest = enumerator.Remaining();
    // 'rest' would contain elements in 'sequence' start at the 3rd element
}

我正在考虑一种单链表的集合,因此应该有一种方法来表示任何剩余元素,对吧?我看不到任何方法可以做到这一点 IEnumerable<T> 或者 IEnumerator<T>, ,所以它可能与潜在无界、不确定的元素序列的概念不兼容。

有帮助吗?

解决方案

如果你必须使用IEnumerator<T>而不是IEnumerable<T>(所有良好的扩展方法)这里有两个简单的方法。

这一次只能被列举单个时间(和被绑定到原始枚举,可以用异常结束该装置了,如果另一线程改变源列表):

public static IEnumerable<T> Remaining<T>( this IEnumerator<T> value ) {
    while( value.MoveNext() ) {
        yield return value.Current;
    }
}

而这其中建立一个列表,可反复枚举(并从原来的枚举器断开,所以你不必担心你的源IEnumerable的改变):

public static IEnumerable<T> Remaining<T>( this IEnumerator<T> value ) {
    List<T> list = new List<T>();
    while( value.MoveNext() ) list.Add( value.Current );

    return list;
}

其他提示

跳过 您要使用两种方法:

IEnumerable<int> sequence = ...
IEnumerable<int> pair = sequence.Take(2); //First two elements
IEnumerable<int> remaining = sequence.Skip(2);

如果你想利用一个IEnumerator<T>并获得代表该序列的其余部分IEnumerable<T>,从字面上看,你将不得不做一些魔术到那里。

这样做的原因是,在一般意义上,可枚举可以列举多次,而枚举不能,它只是由本身那些“多次”中的一个。

首先,你可以尝试找出什么类型的集合你处理,从而对原有的枚举其余的顶部返回一个适当的枚举。你要去的理由。

或者......你可以缓存枚举的其余部分到一个新的收集和返回。当然,这会消耗你原来的枚举,不管它可能是,并且可能是昂贵的,在时间或内存方面。

或者......你可以做什么几个建议,实际上并不返回普查员,而是使用Skip和Take可枚举类的方法来回报你想要的东西。这将返回一个新的枚举,使得每个被枚举时,它将枚举原始枚举,跳过前两项,而产生的其余部分。

让我改写最后一段。如果你不尝试将IEnumerator<T>的其余部分恢复为一个新的枚举,而只是处理原始集合,它变得更容易对付。

下面是一些缓存元素代码。它的好处是,如果你得到的枚举产生2名或更多普查员(甚至只是1),然后让枚举走出去的范围,作为普查员开始通过元素移动,它将让垃圾回收开始收集已经由通过元素。

在换句话说,如果你这样做:

var enumerable = enumerator.Remaining();
var enumerator1 = enumerable.GetEnumerator();
var enumerator2 = enumerable.GetEnumerator();

enumerator1.MoveNext();
enumerator2.MoveNext();
<-- at this point, enumerable is no longer used, and the first (head) element
    of the enumerable is no longer needed (there's no way to get to it)
    it can be garbage collected.

当然,如果你保持枚举的周围,并列举了其中的所有元素,会产生从原来的枚举所有的元素,正如我说的,可以是昂贵的内存拷贝。

总之,这里的代码。它不是线程安全的:

using System;
using System.Collections.Generic;
using System.Collections;

namespace SO2829956
{
    public class EnumeratorEnumerable<T> : IEnumerable<T>
    {
        private class Node
        {
            public T Value;
            public Node Next;
        }

        private class Enumerator : IEnumerator<T>
        {
            private IEnumerator<T> _Enumerator;
            private Node _Current;

            public Enumerator(IEnumerator<T> enumerator, Node headElement)
            {
                _Enumerator = enumerator;
                _Current = headElement;
            }

            public T Current
            {
                get { return _Current.Value; }
            }

            public void Dispose()
            {
                _Enumerator.Dispose();
            }

            object IEnumerator.Current
            {
                get { return Current; }
            }

            public bool MoveNext()
            {
                if (_Current.Next != null)
                {
                    _Current = _Current.Next;
                    return true;
                }
                else if (_Enumerator.MoveNext())
                {
                    _Current.Next = new Node
                    {
                        Value = _Enumerator.Current
                    };
                    _Current = _Current.Next;
                    return true;
                }
                else
                {
                    _Enumerator.Dispose();
                    return false;
                }
            }

            public void Reset()
            {
                throw new NotImplementedException();
            }
        }

        private IEnumerator<T> _Enumerator;
        private Node _FirstElement;

        public EnumeratorEnumerable(IEnumerator<T> enumerator)
        {
            _Enumerator = enumerator;
            _FirstElement = new Node
            {
                Next = null,
                Value = enumerator.Current
            };
        }

        public IEnumerator<T> GetEnumerator()
        {
            return new Enumerator(_Enumerator, _FirstElement);
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }

    public static class EnumeratorExtensions
    {
        public static IEnumerable<T> Remaining<T>(
            this IEnumerator<T> enumerator)
        {
            return new EnumeratorEnumerable<T>(enumerator);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<int> values = new List<int> { 1, 2, 3, 4, 5 };
            IEnumerator<int> enumerator = values.GetEnumerator();
            enumerator.MoveNext();
            enumerator.MoveNext();

            var enumerable = enumerator.Remaining();
            foreach (var i in enumerable)
                Console.Out.WriteLine(i);
            foreach (var i in enumerable)
                Console.Out.WriteLine(i);
        }
    }
}

在运行此程序的输出是:

3
4
5
3
4
5

如果你的目标是能够在一个foreach直接使用IEnumerator<T>,我建议是这样的:

public struct WrappedEnumerator<T>
{
    T myEnumerator;
    public T GetEnumerator() { return myEnumerator; }
    public WrappedEnumerator(T theEnumerator) { myEnumerator = theEnumerator; }
}
public static class AsForEachHelper
{
    static public WrappedEnumerator<IEnumerator<T>> AsForEach<T>(this IEnumerator<T> theEnumerator)
        { return new WrappedEnumerator<IEnumerator<T>>(theEnumerator);}

    static public WrappedEnumerator<System.Collections.IEnumerator> AsForEach(this System.Collections.IEnumerator theEnumerator) 
        { return new WrappedEnumerator<System.Collections.IEnumerator>(theEnumerator); }

    [Obsolete("Structs implementing IEnumerator<T> should be boxed before use", false)]
    static public WrappedEnumerator<System.Collections.IEnumerator> AsForEach<T>(this T theEnumerator) where T : struct, System.Collections.IEnumerator 
    { return new WrappedEnumerator<System.Collections.IEnumerator>(theEnumerator) ; }
}

如果foo是类型IEnumeratorIEnumerator<T>,或者任何类型从任一来源的,可以简单地说foreach (whatever in foo.AsForEach())的变量;如果循环早期退出,那些没有读过任何项目将保留在枚举。但是请注意,像myEnumerator Foo=someList.GetEnumerator()的陈述,其中someListList<T>将定义myEnumerator作为结构类型,它是与所述WrappedEnumerator<T>方法不兼容。如果一个人真的很勇敢,一个可以删除Obsolete标签(或更改其false参数),允许使用与拆箱统计员AsForEach的,但应该注意,结构类型枚举调用AsForEach可能采取列举的快照状态,和计数该快照可能不会影响原始的状态。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top