IEnumerable <T> nasıl uygularım


124

Genel olmayan IEnumerable'ın nasıl uygulanacağını biliyorum, şöyle:

using System;
using System.Collections;

namespace ConsoleApplication33
{
    class Program
    {
        static void Main(string[] args)
        {
            MyObjects myObjects = new MyObjects();
            myObjects[0] = new MyObject() { Foo = "Hello", Bar = 1 };
            myObjects[1] = new MyObject() { Foo = "World", Bar = 2 };

            foreach (MyObject x in myObjects)
            {
                Console.WriteLine(x.Foo);
                Console.WriteLine(x.Bar);
            }

            Console.ReadLine();
        }
    }

    class MyObject
    {
        public string Foo { get; set; }
        public int Bar { get; set; }
    }

    class MyObjects : IEnumerable
    {
        ArrayList mylist = new ArrayList();

        public MyObject this[int index]
        {
            get { return (MyObject)mylist[index]; }
            set { mylist.Insert(index, value); }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return mylist.GetEnumerator();
        }
    }
}

Bununla birlikte, IEnumerable'ın genel bir sürümü olduğunu da fark ettim IEnumerable<T>, ancak bunu nasıl uygulayacağımı çözemiyorum.

Kullanım using System.Collections.Generic;yönergelerime ekler ve sonra değiştirirsem:

class MyObjects : IEnumerable

için:

class MyObjects : IEnumerable<MyObject>

Ve sonra sağ tıklayıp IEnumerable<MyObject>seçin Implement Interface => Implement Interface, Visual Studio aşağıdaki kod bloğunu yardımcı bir şekilde ekler:

IEnumerator<MyObject> IEnumerable<MyObject>.GetEnumerator()
{
    throw new NotImplementedException();
}

GetEnumerator();Yöntemden genel olmayan IEnumerable nesnesini döndürmek bu sefer işe yaramıyor, öyleyse buraya ne yazmalıyım? CLI artık genel olmayan uygulamayı yok sayıyor ve foreach döngüsü sırasında dizim üzerinden numaralandırmaya çalıştığında doğrudan genel sürüme yöneliyor.

Yanıtlar:


149

Eğer gibi genel bir koleksiyon kullanmayı tercih ederse List<MyObject>yerine ArrayList, bunu bulacaksınız List<MyObject>kendinizin kullanabileceği hem jenerik ve genel olmayan enumerator'ler sağlayacaktır.

using System.Collections;

class MyObjects : IEnumerable<MyObject>
{
    List<MyObject> mylist = new List<MyObject>();

    public MyObject this[int index]  
    {  
        get { return mylist[index]; }  
        set { mylist.Insert(index, value); }  
    } 

    public IEnumerator<MyObject> GetEnumerator()
    {
        return mylist.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

1
Jenerik olmayan uygulamada, geri dönmek this.GetEnumerator()ile basitçe geri dönmek arasında bir fark var GetEnumerator()mı?
Tanner Swett

1
@TannerSwett Fark yok.
Monroe Thomas

Siz de miras alabilirsiniz Collection<MyObject>ve sonra herhangi bir özel kod yazmanız gerekmez.
John Alexiou

@ ja72 Ya zaten başka bir temel sınıftan miras alıyorsanız ve <MyObject> Koleksiyonundan miras alamıyorsanız?
Monroe Thomas

1
@MonroeThomas - o zaman cevabınızda gösterildiği gibi sarmalamanız List<T>ve uygulamanız IEnumerable<T>gerekir.
John Alexiou

69

Muhtemelen açıkça uygulanmasını istemiyorsunuz IEnumerable<T>(gösterdiğiniz şey budur).

Genel model, aşağıdakilerin açıkça uygulanmasında IEnumerable<T>'s kullanmaktır :GetEnumeratorIEnumerable

class FooCollection : IEnumerable<Foo>, IEnumerable
{
    SomeCollection<Foo> foos;

    // Explicit for IEnumerable because weakly typed collections are Bad
    System.Collections.IEnumerator IEnumerable.GetEnumerator()
    {
        // uses the strongly typed IEnumerable<T> implementation
        return this.GetEnumerator();
    }

    // Normal implementation for IEnumerable<T>
    IEnumerator<Foo> GetEnumerator()
    {
        foreach (Foo foo in this.foos)
        {
            yield return foo;
            //nb: if SomeCollection is not strongly-typed use a cast:
            // yield return (Foo)foo;
            // Or better yet, switch to an internal collection which is
            // strongly-typed. Such as List<T> or T[], your choice.
        }

        // or, as pointed out: return this.foos.GetEnumerator();
    }
}

2
Bu, SomeCollection <T> IEnumerable <T> 'yi halihazırda uygulamıyorsa veya numaralandırma dizisinde döndürülmeden önce her öğeyi dönüştürmeniz veya başka bir işlem yapmanız gerekiyorsa iyi bir çözümdür. Bu koşullardan hiçbiri doğru değilse, bu durumda this.foos.GetEnumerator () döndürmek muhtemelen daha etkilidir;
Monroe Thomas

@MonroeThomas: kabul etti. OP'nin hangi koleksiyonu kullandığı hakkında hiçbir fikriniz yok.
user7116

8
İyi bir nokta. Ancak uygulama IEnumerablegereksizdir ( IEnumerable<T>zaten ondan miras alınır).
rsenna

@rsenna bu doğru, ancak IList gibi .NET arayüzlerinin bile arayüzleri yedekli olarak uyguladığını aklınızda bulundurun, bu okunabilirlik için - public interface IList: ICollection, IEnumerable
David Klempfner

@Backwards_Dave Aslında (hala) gereksiz gürültü eklediği için daha az okunabilir olduğunu düşünüyorum, ancak ne söylediğini anlıyorum ve bunun geçerli bir fikir olduğunu düşünüyorum.
rsenna

24

Bunu neden elle yapıyorsun? yield returnyineleyicileri işlemenin tüm sürecini otomatikleştirir. ( Derleyici tarafından oluşturulan koda bir bakış da dahil olmak üzere blogumda da bunun hakkında yazdım ).

Bunu gerçekten kendiniz yapmak istiyorsanız, genel bir numaralandırıcı da döndürmelisiniz. ArrayListJenerik olmadığı için artık kullanamazsınız . List<MyObject>Bunun yerine a olarak değiştirin . Elbette bu MyObject, koleksiyonunuzda yalnızca tür nesnelerin (veya türetilmiş türlerin) olduğunu varsayar .


2
+1, tercih edilen modelin genel arayüzü uygulamak ve genel olmayan arayüzü açıkça uygulamak olduğuna dikkat edilmelidir. Getiri getirisi, genel arayüz için en doğal çözümdür.
user7116

5
OP numaralandırıcıda bazı işlemler yapmayacaksa, getiri dönüşünü kullanmak başka bir durum makinesinin ek yükünü ekler. OP, temeldeki bir genel koleksiyon tarafından sağlanan bir numaralandırıcı döndürmelidir.
Monroe Thomas

@MonroeThomas: Haklısın. Hakkında yazmadan önce soruyu çok dikkatli okumadım yield return. Yanlış bir şekilde bazı özel işlemler olduğunu varsaydım.
Anders Abel

4

Jeneriklerle çalışıyorsanız, ArrayList yerine List'i kullanın. List tam olarak ihtiyacınız olan GetEnumerator yöntemine sahiptir.

List<MyObject> myList = new List<MyObject>();

0

mylist'i a yapmak List<MyObject>, seçeneklerden biri


0

O Not IEnumerable<T>Allready tarafından uygulanan System.Collectionsbaşka yaklaşım türetmek yani MyObjectssınıfından System.Collectionsbir temel sınıf (aynı belgeler ):

System.Collections: Genel bir koleksiyon için temel sınıfı sağlar.

Daha sonra kendi implemenation sanal geçersiz kılmak için yapabileceğiniz System.Collectionsözel davranışı sağlamak için yöntemler (sadece ClearItems, InsertItem, RemoveItem, ve SetItembirlikte Equals, GetHashCodeve ToStringgelen Object). List<T>Kolayca genişletilebilir olacak şekilde tasarlanmayanın aksine .

Misal:

public class FooCollection : System.Collections<Foo>
{
    //...
    protected override void InsertItem(int index, Foo newItem)
    {
        base.InsertItem(index, newItem);     
        Console.Write("An item was successfully inserted to MyCollection!");
    }
}

public static void Main()
{
    FooCollection fooCollection = new FooCollection();
    fooCollection.Add(new Foo()); //OUTPUT: An item was successfully inserted to FooCollection!
}

collectionNadiren gerçekleşen özel toplama davranışına ihtiyaç duyulduğunda tavsiye edilen durumdan sürmenin tavsiye edildiğini lütfen unutmayın . bkz kullanımını .

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.