programing

빈 IEnumerator 반환

copyandpastes 2021. 1. 16. 11:00
반응형

빈 IEnumerator 반환


무엇보다도 "public IEnumerator GetEnumerator ()"메서드를 구현하는 인터페이스가 있으므로 foreach 문에서 인터페이스를 사용할 수 있습니다.

이 인터페이스를 여러 클래스에서 구현하고 그중 하나에서 빈 IEnumerator를 반환하고 싶습니다. 지금은 다음과 같이합니다.

public IEnumerator GetEnumerator()
{
    ArrayList arr = new ArrayList();
    return arr.GetEnumerator();
}

그러나 나는 이것을 추악한 해킹이라고 생각하고 빈 IEnumerator를 반환하는 더 좋은 방법이 있다고 생각합니다. 거기 있어요?


이것은 C # 2에서 간단합니다.

public IEnumerator GetEnumerator()
{
    yield break;
}

yield break컴파일러가 반복기 블록으로 처리하도록 하려면 명령문 이 필요합니다 .

이것은 "사용자 정의"빈 반복자보다 덜 효율적이지만 코드가 더 간단합니다.


프레임 워크에는 추가 기능이 있습니다.

public static class Enumerable
{
    public static IEnumerable<TResult> Empty<TResult>();
}

이것을 사용하여 다음과 같이 작성할 수 있습니다.

var emptyEnumerable = Enumerable.Empty<int>();
var emptyEnumerator = Enumerable.Empty<int>().GetEnumerator();

IEnumerator를 구현하는 더미 클래스를 구현하고 그 인스턴스를 반환 할 수 있습니다.

class DummyEnumerator : IEnumerator
{
    public object Current
    {
        get
        {
            throw new InvalidOperationException();
        }
    }

    public bool MoveNext()
    {
        return false;
    }

    public void Reset()
    {
    }
}

나는 궁금해서 조금 더 나아 갔다. 나는 검사 방법을 비교하는 방법을 효율적 것을 시험했다 yield break, Enumerable.Emtpy및 사용자 정의 클래스를.

dotnetfiddle https://dotnetfiddle.net/vTkmcQ 에서 확인 하거나 아래 코드를 사용할 수 있습니다.

19 만 번의 반복을 사용한 많은 dotnetfiddle 실행 중 하나의 결과는 다음과 같습니다.

수익 브레이크 : 00 : 00 : 00.0210611

Enumerable.Empty () : 00 : 00 : 00.0192563

EmptyEnumerator 인스턴스 : 00 : 00 : 00.0012966

using System;
using System.Diagnostics;
using System.Collections;
using System.Linq;

public class Program
{
    private const int Iterations = 190000;
    public static void Main()
    {
        var sw = new Stopwatch();

        sw.Start();
        for (int i = 0; i < Iterations; i++)
        {
            IEnumerator enumerator = YieldBreak();
            while(enumerator.MoveNext())
            {
                throw new InvalidOperationException("Should not occur");
            }           
        }
        sw.Stop();

        Console.WriteLine("Yield break: {0}", sw.Elapsed);

        GC.Collect();

        sw.Restart();
        for (int i = 0; i < Iterations; i++)
        {
            IEnumerator enumerator = Enumerable.Empty<object>().GetEnumerator();
            while(enumerator.MoveNext())
            {
                throw new InvalidOperationException("Should not occur");
            }           
        }
        sw.Stop();

        Console.WriteLine("Enumerable.Empty<T>(): {0}", sw.Elapsed);

        GC.Collect();

        sw.Restart();
        var instance = new EmptyEnumerator();
        for (int i = 0; i < Iterations; i++)
        {
            while(instance.MoveNext())
            {
                throw new InvalidOperationException("Should not occur");
            }           
        }
        sw.Stop();

        Console.WriteLine("EmptyEnumerator instance: {0}", sw.Elapsed);
    }

    public static IEnumerator YieldBreak()
    {
        yield break;
    }

    private class EmptyEnumerator : IEnumerator
    {
        //public static readonly EmptyEnumerator Instance = new EmptyEnumerator();

        public bool MoveNext()
        {
            return false;
        }

        public void Reset()
        {
        }

        public object Current { get { return null; } }
    }
}

내가 사용하는 방법은 빈 배열의 열거자를 사용하는 것입니다.

public IEnumerator GetEnumerator() {
    return new object[0].GetEnumerator();
}

일반 IEnumerator 또는 IEnumerable (적절한 유형의 배열 사용)에도 사용할 수 있습니다.


IEnumerable 인터페이스와 IEnumerable을 구현하고 IEnumerable interfase의 MoveNext 함수에서 false를 반환 할 수 있습니다.

private class EmptyEnumerator : IEnumerator
{


    public EmptyEnumerator()
    {
    }

    #region IEnumerator Members

    public void Reset() { }

    public object Current
    {
        get
        {
            throw new InvalidOperationException();
        }
    }
    public bool MoveNext()
    { return false; }
}


public class EmptyEnumerable : IEnumerable
{

    public IEnumerator GetEnumerator()
    {
        return new EmptyEnumerator();
    }
}

다음과 같이 썼습니다.

public IEnumerator<T> GetEnumerator()
{
    return this.source?.GetEnumerator() ??
            Enumerable.Empty<T>().GetEnumerator();
}

You can make a NullEnumerator which implements the IEnumerator interface. You can just pass an instance off the NullEnumerator.

here is an example of an EmptyEnumerator


Found this question looking for the simplest way to get an empty enumerator. After seeing the answer comparing performance I decided to use the empty enumerator class solution, but mine is more compact than the other examples, and is a generic type, and also provides a default instance so you don't have to create new instances all the time, which should even further improve performance.

class EmptyEnumerator<T> : IEnumerator<T>
{
   public readonly static EmptyEnumerator<T> value = new EmptyEnumerator<T>();
   public T Current => throw new InvalidOperationException();
   object IEnumerator.Current => throw new InvalidOperationException();
   public void Dispose() { }
   public bool MoveNext() => false;
   public void Reset() { }
}

ReferenceURL : https://stackoverflow.com/questions/1714351/return-an-empty-ienumerator

반응형