ฉันจะใช้ IEnumerable <T> ได้อย่างไร


124

ฉันรู้วิธีใช้ IEnumerable ที่ไม่ใช่แบบทั่วไปเช่นนี้:

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

อย่างไรก็ตามฉันสังเกตด้วยว่า IEnumerable มีเวอร์ชันทั่วไปIEnumerable<T>แต่ฉันไม่สามารถหาวิธีใช้งานได้

หากฉันเพิ่มusing System.Collections.Generic;คำสั่งการใช้ของฉันแล้วเปลี่ยน:

class MyObjects : IEnumerable

ถึง:

class MyObjects : IEnumerable<MyObject>

จากนั้นคลิกขวาIEnumerable<MyObject>และเลือกImplement Interface => Implement InterfaceVisual Studio ช่วยเพิ่มบล็อกโค้ดต่อไปนี้:

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

การส่งคืนอ็อบเจ็กต์ IEnumerable ที่ไม่ใช่ทั่วไปจากGetEnumerator();เมธอดไม่ได้ผลในครั้งนี้ฉันจะใส่อะไรที่นี่? ขณะนี้ CLI ละเว้นการใช้งานที่ไม่ใช่แบบทั่วไปและมุ่งตรงไปที่เวอร์ชันทั่วไปเมื่อพยายามระบุผ่านอาร์เรย์ของฉันในระหว่าง foreach loop

คำตอบ:


149

หากคุณเลือกใช้คอลเลคชันทั่วไปเช่นList<MyObject>แทนที่จะใช้ArrayListคุณจะพบว่าList<MyObject>จะมีตัวนับทั้งแบบทั่วไปและแบบไม่ใช้ทั่วไปที่คุณสามารถใช้ได้

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
ในการใช้งานที่ไม่ใช่แบบทั่วไปมีความแตกต่างระหว่างการส่งคืนthis.GetEnumerator()และการส่งคืนGetEnumerator()หรือไม่?
Tanner Swett

1
@TannerSwett ไม่มีความแตกต่าง
Monroe Thomas

คุณอาจได้รับมรดกจากCollection<MyObject>นั้นคุณไม่ต้องเขียนโค้ดที่กำหนดเองใด ๆ
John Alexiou

@ ja72 จะเกิดอะไรขึ้นถ้าคุณได้รับมรดกจากคลาสพื้นฐานอื่นแล้วและไม่สามารถสืบทอดจาก Collection <MyObject> ได้?
Monroe Thomas

1
@MonroeThomas - จากนั้นคุณต้องห่อList<T>และใช้งานIEnumerable<T>ตามที่แสดงในคำตอบของคุณ
John Alexiou

69

คุณอาจไม่ต้องการให้มีการนำไปใช้อย่างชัดเจนIEnumerable<T> (ซึ่งเป็นสิ่งที่คุณได้แสดงไว้)

รูปแบบปกติคือการใช้IEnumerable<T>'s GetEnumeratorในการดำเนินงานที่ชัดเจนของIEnumerable:

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
นี่เป็นทางออกที่ดีหาก SomeCollection <T> ยังไม่ได้ใช้ IEnumerable <T> หรือถ้าคุณต้องการแปลงร่างหรือทำการประมวลผลอื่น ๆ ในแต่ละองค์ประกอบก่อนที่จะส่งคืนในลำดับการแจงนับ หากเงื่อนไขเหล่านี้ไม่เป็นจริงอาจมีประสิทธิภาพมากกว่าที่จะส่งคืน this.foos.GetEnumerator ();
Monroe Thomas

@MonroeThomas: เห็นด้วย ไม่มีเงื่อนงำอะไรที่ OP ใช้
user7116

8
จุดดี. แต่การนำไปใช้IEnumerableนั้นซ้ำซ้อน ( IEnumerable<T>สืบทอดมาแล้ว)
rsenna

@rsenna เป็นความจริงอย่างไรก็ตามโปรดจำไว้ว่าแม้กระทั่งอินเทอร์เฟซ. NET เช่น IList ใช้อินเทอร์เฟซซ้ำซ้อนเพื่อให้สามารถอ่านได้ - อินเทอร์เฟซสาธารณะ IList: ICollection, IEnumerable
David Klempfner

@Backwards_Dave ฉัน (ยัง) คิดว่ามันทำให้อ่านได้น้อยลงเนื่องจากมันเพิ่มเสียงรบกวนที่ไม่จำเป็น แต่ฉันเข้าใจสิ่งที่คุณพูดและคิดว่ามันเป็นความคิดเห็นที่ถูกต้อง
rsenna

24

ทำไมต้องทำด้วยตนเอง? yield returnทำให้กระบวนการทั้งหมดจัดการตัววนซ้ำโดยอัตโนมัติ (ฉันยังเขียนเกี่ยวกับเรื่องนี้ในบล็อกของฉันรวมถึงดูโค้ดที่คอมไพเลอร์สร้างขึ้น)

หากคุณต้องการทำด้วยตัวเองจริงๆคุณต้องส่งคืนตัวแจงนับทั่วไปด้วย คุณจะไม่สามารถใช้งานได้ArrayListอีกต่อไปเนื่องจากไม่ใช่แบบทั่วไป เปลี่ยนเป็นList<MyObject>แทน แน่นอนว่าคุณมีวัตถุประเภทMyObject(หรือประเภทที่ได้รับ) ในคอลเลกชันของคุณเท่านั้น


2
+1 ควรสังเกตว่ารูปแบบที่ต้องการคือการใช้อินเทอร์เฟซทั่วไปและใช้อินเทอร์เฟซที่ไม่ใช่ทั่วไปอย่างชัดเจน ผลตอบแทนเป็นวิธีแก้ปัญหาที่เป็นธรรมชาติที่สุดสำหรับอินเทอร์เฟซทั่วไป
user7116

5
เว้นแต่ OP จะทำการประมวลผลบางอย่างในตัวแจงนับการใช้ผลตอบแทนจะเพิ่มค่าโสหุ้ยของเครื่องสถานะอื่น OP ควรส่งคืนตัวแจงนับที่มาจากคอลเลกชันทั่วไป
Monroe Thomas

@MonroeThomas: คุณพูดถูก yield returnผมไม่ได้อ่านคำถามอย่างระมัดระวังก่อนที่จะเขียนเกี่ยวกับ ฉันเดาผิดว่ามีการประมวลผลที่กำหนดเองบางอย่าง
Anders Abel



0

โปรดทราบว่าแนวทางอื่นที่IEnumerable<T>ใช้ allready System.Collectionsคือการได้มาMyObjectsจากคลาสของคุณSystem.Collectionsเป็นคลาสฐาน ( เอกสารประกอบ ):

System.Collections: จัดเตรียมคลาสพื้นฐานสำหรับคอลเลกชันทั่วไป

เราสามารถทำให้ต่อมา implemenation ของเราเองเพื่อแทนที่เสมือนSystem.Collectionsวิธีการเพื่อให้พฤติกรรมที่กำหนดเอง (เฉพาะClearItems, InsertItem, RemoveItemและSetItemพร้อมด้วยEquals, GetHashCodeและToStringจากObject) แตกต่างจากList<T>ที่ไม่ได้ออกแบบมาให้ขยายได้ง่าย

ตัวอย่าง:

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!
}

โปรดทราบว่าการขับรถจากที่collectionแนะนำเฉพาะในกรณีที่จำเป็นต้องมีพฤติกรรมการเก็บรวบรวมแบบกำหนดเองซึ่งแทบจะไม่เกิดขึ้น ดูการใช้งาน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.