IEnumerator<t></t>



  • I think the most common access interface access to a set of elements is IEnumerable<T> and IEnumerator<T>♪ Almost everything, if not all, the containers support it. This pathterne is good that allows access to absolutely any set, regardless of its internal storage structure. For example, all T[s, containers System.Collections.Genericmethods c yield return etc.

    (1) However, there was a need to process some sequence of elements with the possibility of a return. That is, N+m of elements may be needed for the treatment of the N- element. In general, the solution to this problem is to clone the main terator on the N-style. The Clon is further used only to run up to N+m of the element. The main terator then continues to work with the N-who stopped. This is a good decision that is being implemented very easily. It is sufficient to implement the method Clone Interface ICloneable♪ Usually it's only one line:

    public class MyEnumerator<T> : IEnumerator<T>, ICloneable
    {
        // Во внутренних полях обычно хранится указатель на контейнер и индекс текущего элемента в нем,
        // либо указатель на элемент связанной структуры.
    
    // Реализация методов IEnumerable&lt;T&gt; ...
    
    public object Clone()
    {
        // Простое поверхностное копирование немногочисленных внутренних полей
        return MemberwiseClone();
    }
    

    }

    As an example, class System.CharEnumerator with a description in MSDN of its application and cloning scenario.

    This decision is very simple, universal and cost-effective. However, only two cases of its use, an numerator in the line, have been flawed. System.CharEnumerator) and untapped numerator in the mass:

    static void Main(string[] args)
    {
    {
    // Строка имеет единый System.CharEnumerator, поддерживающий клонирование
    string str = "123";
    IEnumerator<char> enumerator = ((IEnumerable<char>)str).GetEnumerator(); // System.CharEnumerator
    ICloneable cloneable = (ICloneable)enumerator; // System.CharEnumerator
    IEnumerator<char> enumerator2 = (IEnumerator<char>)cloneable.Clone(); // System.CharEnumerator
    }

    {
        // IEnumerator&lt;T&gt; в массивах клонирование не поддерживает
        char[] arr = new char[3];
        IEnumerator&lt;char&gt; enumerator = ((IEnumerable&lt;char&gt;)arr).GetEnumerator(); // System.SZArrayHelper.SZGenericArrayEnumerator&lt;char&gt;
        //ICloneable cloneable = (ICloneable)enumerator; // System.InvalidCastException
    }
    
    {
        // IEnumerator в массивах клонирование поддерживает
        char[] arr = new char[3];
        IEnumerator enumerator = ((IEnumerable)arr).GetEnumerator(); // System.Array.SZArrayEnumerator
        ICloneable cloneable = (ICloneable)enumerator; // System.Array.SZArrayEnumerator
        IEnumerator enumerator2 = (IEnumerator)cloneable.Clone(); // System.Array.SZArrayEnumerator
    }
    
    {
        // IEnumerator от List&lt;T&gt; клонирование не поддерживает
        List&lt;char&gt; lst = new List&lt;char&gt;();
        IEnumerator enumerator = ((IEnumerable)lst).GetEnumerator(); // System.Collections.Generic.List&lt;char&gt;.Enumerator
        //ICloneable cloneable = (ICloneable)enumerator; // System.InvalidCastException
    }
    
    {
        // IEnumerator&lt;T&gt; от List&lt;T&gt; клонирование не поддерживает
        List&lt;char&gt; lst = new List&lt;char&gt;();
        IEnumerator&lt;char&gt; enumerator = ((IEnumerable&lt;char&gt;)lst).GetEnumerator(); // System.Collections.Generic.List&lt;char&gt;.Enumerator
        //ICloneable cloneable = (ICloneable)enumerator; // System.InvalidCastException
    }
    
    {
        // IEnumerator&lt;T&gt; в yield return методе клонирование не поддерживает
        IEnumerator&lt;char&gt; enumerator = ((IEnumerable&lt;char&gt;)YieldReturnMethod()).GetEnumerator(); // tmp.Program.&lt;YieldReturnMethod&gt;d__1
        //ICloneable cloneable = (ICloneable)enumerator; // System.InvalidCastException
    }
    
    {
        // IEnumerator в yield return методе клонирование не поддерживает
        IEnumerator enumerator = ((IEnumerable)YieldReturnMethod()).GetEnumerator(); // tmp.Program.&lt;YieldReturnMethod&gt;d__1
        //ICloneable cloneable = (ICloneable)enumerator; // System.InvalidCastException
    }
    

    }

    static IEnumerable<char> YieldReturnMethod()
    {
    yield return 'a';
    yield return 'b';
    yield return 'c';
    }

    The low level of support ICloneable It's a misunderstanding. It turns out that almost all you have to do is write your own class. IEnumerable<T> and 2nd class IEnumerator<T>♪ It is likely that the most common option will be to pre-receive the whole sequence, retain it in its container and then use its set of classes that operate the necessary interfaces. However, such an option may in some cases be either inapplicable or very resource-intensive.
    I'd like to ask who's thinking about it.



  • The thing is, usually the cloning of an enumerator makes no sense.

    For example, if your data come from the network, there's no point in cloning the enumerator: you can't do the data twice without hanging them (and it's obviously a large and unjustifiable memory flow).

    If your sequence is obtained with a generator. yield returnYou can't incline the data. Worse still, the sequence in the rerun may change (e.g., if the data are issued depends on the outside world).

    The case where the sequence is known in advance, materialized and recorded is known. In this case, you have an interface. IList<T>where you can just remember the current index.


    If you know how long it takes to look forward, it's easy to write a screwdriver.

    class LookaheadEnumerable<T> : IEnumerable<T>
    {
        readonly IEnumerable<T> wrapped;
        readonly int maxLookaheadSize;
    
    public LookaheadEnumerable(IEnumerable&lt;T&gt; wrapped, int maxLookaheadSize)
    {
        this.wrapped = wrapped;
        this.maxLookaheadSize = maxLookaheadSize;
    }
    
    public LookaheadEnumerator&lt;T&gt; GetEnumerator() =&gt;
        new LookaheadEnumerator&lt;T&gt;(wrapped.GetEnumerator(), maxLookaheadSize);
    IEnumerator&lt;T&gt; IEnumerable&lt;T&gt;.GetEnumerator() =&gt; GetEnumerator();
    IEnumerator IEnumerable.GetEnumerator() =&gt; GetEnumerator();
    

    }

    class LookaheadEnumerator<T> : IEnumerator<T>, IEnumerator
    {
    List<T> lookaheadBuffer;
    readonly int maxLookaheadSize;
    readonly IEnumerator<T> wrapped;

    public LookaheadEnumerator(IEnumerator&lt;T&gt; wrapped, int maxLookaheadSize)
    {
        if (maxLookaheadSize &lt; 1)
            throw new ArgumentException("expect positive lookahead size");
        this.maxLookaheadSize = maxLookaheadSize;
        this.wrapped = wrapped;
    }
    
    public T Current =&gt; lookaheadBuffer[0];
    object IEnumerator.Current =&gt; Current;
    public void Dispose() =&gt; wrapped.Dispose();
    
    public bool MoveNext()
    {
        if (lookaheadBuffer == null)
            lookaheadBuffer = new List&lt;T&gt;(maxLookaheadSize);
        else if (lookaheadBuffer.Count &gt; 0)
            lookaheadBuffer.RemoveAt(0);
    
        while (lookaheadBuffer.Count &lt; maxLookaheadSize)
        {
            if (!wrapped.MoveNext())
                break;
            lookaheadBuffer.Add(wrapped.Current);
        }
    
        return lookaheadBuffer.Count &gt; 0;
    }
    
    public void Reset()
    {
        wrapped.Reset();
        lookaheadBuffer = null;
    }
    
    public bool Lookahead(int idx, out T t)
    {
        bool good = idx &gt;= 0 &amp;&amp; idx &lt; lookaheadBuffer.Count;
        t = good ? lookaheadBuffer[idx] : default(T);
        return good;
    }
    

    }

    Example of use:

    var seq = new LookaheadEnumerable<int>(Enumerable.Range(0, 4), 7);
    using (var en = seq.GetEnumerator())
    while (en.MoveNext())
    {
    var value = en.Current;
    int lookehead;
    if (en.Lookahead(2, out lookehead))
    Console.WriteLine($"value: {value}, lookahead + 2: {lookehead}");
    else
    Console.WriteLine($"value: {value}, no lookahead");
    }




Suggested Topics

  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2
  • 2