سؤال

في C #، كيف يحصل المرء على عداد عام من صفيف معين؟

في الكود أدناه، MyArray هي مجموعة من MyType أشياء. أود الحصول عليها MyIEnumerator في الأزياء المعروضة، ولكن يبدو أنني أحصل على عداد فارغ (على الرغم من أنني أؤكد ذلك MyArray.Length > 0).

MyType [ ]  MyArray  =  ... ;
IEnumerator<MyType>  MyIEnumerator
  =  ( MyArray.GetEnumerator() as IEnumerator<MyType> ) ;
هل كانت مفيدة؟

المحلول

يعمل على 2.0+:

((IEnumerable<MyType>)myArray).GetEnumerator()

يعمل على 3.5+ (يتوهم Linqy، أقل فعالية بعض الشيء):

myArray.Cast<MyType>().GetEnumerator()   // returns IEnumerator<MyType>

نصائح أخرى

يمكنك أن تقرر لنفسك ما إذا كانت الصب قبيحة بما يكفي لتبرير مكالمة مكتبة غريبة:

int[] arr;
IEnumerator<int> Get1()
{
    return ((IEnumerable<int>)arr).GetEnumerator();    // <-- 1 non-local call
    // L_0001: ldarg.0 
    // L_0002: ldfld int32[] foo::arr
    // L_0007: castclass [mscorlib]System.Collections.Generic.IEnumerable`1<int32>
    // L_000c: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    // L_0011: stloc.0 
}
IEnumerator<int> Get2()
{
    return arr.AsEnumerable().GetEnumerator();    // <-- 2 non-local calls
    // L_0001: ldarg.0 
    // L_0002: ldfld int32[] foo::arr
    // L_0007: call class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0> [System.Core]System.Linq.Enumerable::AsEnumerable<int32>(class [mscorlib]System.Collections.Generic.IEnumerable`1<!!0>)
    // L_000c: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
    // L_0011: stloc.0 
}

ولإكمال، يجب أن يلاحظ المرء أيضا أن ما يلي غير صحيح - وسوف تعطل في وقت التشغيل - ل T[] يختار عدم-نوعي IEnumerable واجهة لتنفيذها الافتراضي (أي غير واضحة) ل GetEnumerator().

IEnumerator<int> NoGet()   // error - do not use
{
    return (IEnumerator<int>)arr.GetEnumerator();
    // L_0001: ldarg.0 
    // L_0002: ldfld int32[] foo::arr
    // L_0007: callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator()
    // L_000c: castclass [mscorlib]System.Collections.Generic.IEnumerator`1<int32>
    // L_0011: stloc.0 
}

الغموض هو، لماذا لا SZGenericArrayEnumerator<T> ورث من SZArrayEnumerator- الفئة الداخلية التي تم وضع علامة حاليا على "مختومة" - نظرا لأن هذا سيتيح العداد العام (Covariant) الذي سيتم إرجاعه افتراضيا؟

لأنني لا أحب الصب، وتحديث القليل:

your_array.AsEnumerable().GetEnumerator();

لجعلها نظيفة قدر الإمكان، أحب السماح للمترجم بإجراء كل هذا العمل. لا توجد يلقي (لذلك فعلا نوع آمن). لا يتم استخدام مكتبات طرف ثالث (SYSTEM.LINQ) (بدون وقت تشغيل النفقات العامة).

    public static IEnumerable<T> GetEnumerable<T>(this T[] arr)
    {
        return arr;
    }

// واستخدام الرمز:

    String[] arr = new String[0];
    arr.GetEnumerable().GetEnumerator()

هذا يستفيد من سحر مترجم يحافظ على كل شيء نظيفا.

النقطة الأخرى التي يجب ملاحظتها هي أن إجابتي هي الإجابة الوحيدة التي ستقوم بالتحقق من وقت الترجمة.

بالنسبة لأي من الحلول الأخرى إذا تغير نوع "ARR"، فسوف يترجم رمز الاتصال، وتفشل في وقت التشغيل، مما يؤدي إلى علة وقت التشغيل.

سوف تتسبب إجابتي في عدم تجميع التعليمات البرمجية وبالتالي لدي فرصة أقل للشحن خطأ في التعليمات البرمجية، حيث ستشير لي أنني أستخدم النوع الخطأ.

yourarray.oftype (). getenumerator ()؛

قد يؤدي أفضل قليلا، لأنه يتعين عليه فقط التحقق من النوع، وليس يلقي.

    MyType[] arr = { new MyType(), new MyType(), new MyType() };

    IEnumerable<MyType> enumerable = arr;

    IEnumerator<MyType> en = enumerable.GetEnumerator();

    foreach (MyType item in enumerable)
    {

    }

ما يمكنك القيام به، بالطبع، هو مجرد تطبيق العداد العام الخاص بك للحصول على صفائف.

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

namespace SomeNamespace
{
    public class ArrayEnumerator<T> : IEnumerator<T>
    {
        public ArrayEnumerator(T[] arr)
        {
            collection = arr;
            length = arr.Length;
        }
        private readonly T[] collection;
        private int index = -1;
        private readonly int length;

        public T Current { get { return collection[index]; } }

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

        public bool MoveNext() { index++; return index < length; }

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

        public void Dispose() {/* Nothing to dispose. */}
    }
}

هذا هو أكثر أو أقل تساوي .NET redemenation من szgenericarrayenumeratoru003CT> كما ذكرها Glenn Slayden. يجب عليك بالطبع القيام بذلك فقط، هو الحالات التي يستحق فيها هذا الجهد. في معظم الحالات ليست كذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top