سؤال

لدي واجهة، من بين أمور أخرى، تنفذ طريقة "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. أو استخدم الرمز أدناه.

وكانت نتيجة واحدة من العديد من العديد من DotNetFiddle باستخدام 190 000 التكرارات كانت:

استراحة العائد: 00: 00: 00.0210611

empliable.empty (): 00: 00: 00.0192563

مثيل playenumerator: 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 (استخدم مجموعة من النوع المناسب)

يمكنك تنفيذ واجهة ienumerator و ienumerable، والعودة كاذبة من وظيفة movenext لل interfase iEnumperable

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();
}

يمكنك جعل Nullenumerator ينفذ واجهة IEnumerator. يمكنك فقط تمرير مثيل خارج Nullenumerator.

هنا هو مثال على pastryumerator

وجدت هذا السؤال يبحث عن أبسط طريقة للحصول على عداد فارغ. بعد رؤية الإجابة مقارنة الأداء، قررت استخدام محلول فئة العداد الفارغ، لكن الألغام أكثر إحكاما من الأمثلة الأخرى، وهو نوع عام، ويوفر أيضا مثيلا افتراضيا حتى لا تضطر إلى إنشاء مثيلات جديدة كل الوقت، والتي يجب أن تؤدي إلى تحسين الأداء.

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() { }
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top