bir diziden genel numaralandırıcı elde edin


92

C # 'da, belirli bir diziden genel bir numaralandırıcı nasıl elde edilir?

Aşağıdaki kodda MyArraybir dizi MyTypenesne var. MyIEnumeratorGösterilen şekilde elde etmek istiyorum , ancak görünüşe göre boş bir numaralayıcı elde ediyorum (bunu doğrulamama rağmen MyArray.Length > 0).

MyType[] MyArray = ... ;
IEnumerator<MyType> MyIEnumerator = MyArray.GetEnumerator() as IEnumerator<MyType>;

Merak ediyorum, neden numaralandırıcıyı almak istiyorsun?
David Klempfner

1
@Backwards_Dave benim durumumda tek iş parçacıklı bir ortamda, her birinin Async bir şekilde bir kez işlenmesi gereken dosyaların bir listesi var.
Arttırmak

Yanıtlar:


104

2.0+ üzerinde çalışır:

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

3.5+ üzerinde çalışır (süslü LINQy, biraz daha az verimli):

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

3
LINQy kodu aslında, dizi için bir numaralandırıcı yerine Cast yönteminin sonucu için bir numaralandırıcı döndürür ...
Guffa

1
Guffa: Numaralandırıcılar salt okunur erişim sağladığından, kullanım açısından büyük bir fark oluşturmaz.
Mehrdad Afshari

8
@Mehrdad'in ima ettiği gibi, Kullanmak LINQ/Castoldukça farklı çalışma zamanı davranışına sahiptir, çünkü dizinin her bir öğesi fazladan bir set MoveNextve Currentnumaralandırıcı dönüşünden geçecektir ve bu, dizi çok büyükse performansı etkileyebilir. Her durumda, ilk etapta uygun numaralandırıcıyı elde ederek (yani cevabımda gösterilen iki yöntemden birini kullanarak) problem tamamen ve kolayca önlenebilir.
Glenn Slayden

7
İlki daha iyi seçenektir! Hiç kimse, tamamen gereksiz olan "süslü LINQy" sürümüyle kendini aldatmasın.
henon

@GlennSlayden Sadece büyük diziler için değil, her boyuttaki diziler için de yavaştır. Yani myArray.Cast<MyType>().GetEnumerator()en içteki döngünüzde kullanırsanız , küçük diziler için bile sizi önemli ölçüde yavaşlatabilir.
Evgeniy Berezovsky

54

Yayınlamanın gereksiz bir kütüphane çağrısı gerektirecek kadar çirkin olup olmadığına kendiniz karar verebilirsiniz:

int[] arr;
IEnumerator<int> Get1()
{
    return ((IEnumerable<int>)arr).GetEnumerator();  // <-- 1 non-local call

    // ldarg.0 
    // ldfld int32[] foo::arr
    // castclass System.Collections.Generic.IEnumerable`1<int32>
    // callvirt instance class System.Collections.Generic.IEnumerator`1<!0> System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
}

IEnumerator<int> Get2()
{
    return arr.AsEnumerable().GetEnumerator();   // <-- 2 non-local calls

    // ldarg.0 
    // ldfld int32[] foo::arr
    // call class System.Collections.Generic.IEnumerable`1<!!0> System.Linq.Enumerable::AsEnumerable<int32>(class System.Collections.Generic.IEnumerable`1<!!0>)
    // callvirt instance class System.Collections.Generic.IEnumerator`1<!0> System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()
}

Ve tamlık için, aşağıdakilerin doğru olmadığı ve çalışma zamanında çökeceği de unutulmamalıdır T[], çünkü varsayılan (yani açık olmayan) uygulaması için genel olmayanIEnumerable arabirimi seçer GetEnumerator().

IEnumerator<int> NoGet()                    // error - do not use
{
    return (IEnumerator<int>)arr.GetEnumerator();

    // ldarg.0 
    // ldfld int32[] foo::arr
    // callvirt instance class System.Collections.IEnumerator System.Array::GetEnumerator()
    // castclass System.Collections.Generic.IEnumerator`1<int32>
}

Gizem şu ki, neden şu anda 'mühürlenmiş' olarak işaretlenmiş bir iç sınıftan SZGenericArrayEnumerator<T>miras almıyor, SZArrayEnumeratorçünkü bu (kovaryant) genel numaralandırıcının varsayılan olarak döndürülmesine izin veriyor?


Ekstra köşeli parantez kullanmanın bir nedeni var, ((IEnumerable<int>)arr)ancak yalnızca bir dizi parantez içinde (IEnumerator<int>)arr?
David Klempfner

1
@Backwards_Dave Evet, üstteki doğru örneklerle alttaki yanlış örnekler arasındaki farkı yaratan şey budur. Kontrol operatör öncelik ait C # tekli "cast" operatörü.
Glenn Slayden

24

Oyuncu seçimi yapmayı sevmediğim için küçük bir güncelleme:

your_array.AsEnumerable().GetEnumerator();

AsEnumerable () aynı zamanda döküm de yapar, bu yüzden hala döküm yapıyorsunuz :) Belki your_array.OfType <T> () .GetEnumerator ();
Mladen B.

1
Aradaki fark, çalışma zamanında yapılan açık bir dönüştürme yerine derleme zamanında yapılan örtük bir dönüştürme olmasıdır. Bu nedenle, tür yanlışsa, çalışma zamanı hataları yerine derleme zamanı hataları alırsınız.
Hank Schultz

@HankSchultz eğer tür yanlışsa your_array.AsEnumerable(), ilk etapta derlenmez, çünkü AsEnumerable()yalnızca uygulayan türlerin örneklerinde kullanılabilir IEnumerable.
David Klempfner

5

Olabildiğince temiz hale getirmek için derleyicinin tüm işi yapmasına izin vermeyi seviyorum. Yayın yok (bu yüzden aslında tür açısından güvenli). Hiçbir üçüncü taraf Kitaplığı (System.Linq) kullanılmaz (Çalışma zamanı ek yükü yoktur).

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

// Ve kodu kullanmak için:

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

Bu, her şeyi temiz tutan bazı derleyici sihrinden yararlanır.

Dikkat edilmesi gereken diğer bir nokta ise, derleme zamanı kontrolünü yapacak tek cevabımın cevabım olmasıdır.

Diğer çözümlerden herhangi biri için "arr" türü değişirse, kod çağırma derlenir ve çalışma zamanında başarısız olur, bu da bir çalışma zamanı hatasıyla sonuçlanır.

Cevabım, kodun derlenmemesine neden olacak ve bu nedenle, yanlış türü kullandığımı bana bildireceği için kodumdaki bir hatayı gönderme şansım daha az olacak.


GlennSlayden ve MehrdadAfshari tarafından gösterildiği gibi döküm de derleme zamanı kanıtıdır.
t3chb0t

1
@ t3chb0t hayır onlar değil. Zamanında iş çünkü bunu biliyoruz Foo[]uygular IEnumerable<Foo>ama bu gördüğüm değiştirirse derleme zamanında tespit edilmeyecektir. Açık yayınlar asla derleme zamanı kanıtı değildir. Bunun yerine, IEnumerable <Foo> olarak diziyi atama / döndürme, derleme zamanı kanıtı olan örtük çevirmeyi kullanır.
Toxantron

@Toxantron IEnumerable <T> 'ye açık bir çevirme, çevirmeye çalıştığınız tür IEnumerable uygulamadıkça derlenmez. "Ama bu değişirse" dediğinizde, ne demek istediğinize bir örnek verebilir misiniz?
David Klempfner

Tabii ki derliyor. Ne yıllardan bu ınvalidcastexception içindir. Derleyiciniz sizi belirli bir dökümün çalışmadığı konusunda uyarabilir. Deneyin var foo = (int)new object(). Çok iyi derler ve çalışma zamanında çöker.
Toxantron

2

YourArray.OfType (). GetEnumerator ();

biraz daha iyi performans gösterebilir, çünkü sadece türü kontrol etmesi gerekir, döküm yapmaz.


Açıkça kullanarak türünü belirtmek zorunda OfType<..type..>()arasında benim durumumda en azından -double[][]
M. Mimpen

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

    IEnumerable<MyType> enumerable = arr;

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

    foreach (MyType item in enumerable)
    {

    }

Lütfen yukarıdaki kodun ne
işe yarayacağını

Bu çok daha yüksek oy olmalı! Derleyicinin örtük dökümünü kullanmak en temiz ve en kolay çözümdür.
Toxantron

-1

Elbette yapabileceğiniz şey, diziler için kendi genel numaralandırıcınızı uygulamaktır.

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. */}
    }
}

Bu, Glenn Slayden tarafından belirtildiği gibi SZGenericArrayEnumerator <T> 'nin .NET uygulamasına aşağı yukarı eşittir. Elbette sadece bunu yapmalısınız, bu çabaya değecek durumlarda. Çoğu durumda değildir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.