ความแตกต่างระหว่าง IEnumerable และ Array, IList และ List?


92

อะไรคือความแตกต่างระหว่างIEnumerableและArray?

อะไรคือความแตกต่างระหว่างIListและList?

สิ่งเหล่านี้ดูเหมือนจะมีหน้าที่เหมือนกัน

คำตอบ:


101

IEnumerable มีฟังก์ชัน "ทำซ้ำได้" เพียงเล็กน้อยเท่านั้น คุณสามารถสำรวจลำดับได้ แต่ก็เกี่ยวกับมัน สิ่งนี้มีข้อเสีย - ตัวอย่างเช่นการนับองค์ประกอบโดยใช้ IE ไม่ได้มีประสิทธิภาพมากหรือเพื่อให้ได้องค์ประกอบที่ n - แต่ก็มีข้อดีเช่นกันเช่น IEnumerable อาจเป็นลำดับที่ไม่มีที่สิ้นสุดเช่นลำดับของไพรม์

Array คือคอลเล็กชันขนาดคงที่พร้อมการเข้าถึงแบบสุ่ม (เช่นคุณสามารถจัดทำดัชนีได้)

List คือคอลเล็กชันขนาดตัวแปร (เช่นคุณสามารถเพิ่มและลบองค์ประกอบ) ด้วยการเข้าถึงแบบสุ่ม

IList เป็นอินเทอร์เฟซที่แสดงรายการฟังก์ชันการทำงาน (นับเพิ่มลบการเข้าถึงดัชนี) ออกจากคลาสคอนกรีตต่างๆเช่น List, BindingList, ObservableCollection เป็นต้น


2
ตามคำตอบที่ยอมรับได้ที่นี่stackoverflow.com/questions/1826658/… . การนับบน IEnumerable เกือบจะเป็นช่องทางเดียวกันกับที่พยายามเรียก Count on ICollection
Karsten

15
ใช่และไม่. วิธีการขยาย Count () จะตรวจสอบว่า IEnumerable เป็น ICollection หรือไม่และเรียกคุณสมบัติ Count ถ้าเป็น หาก IEnumerable ไม่ใช่ ICollection แล้ว Count () จะกลับไปทำซ้ำตามลำดับ บางทีฉันควรจะบอกว่า "มันไม่มีประสิทธิภาพมากในการนับองค์ประกอบโดยใช้IEnumerable เท่านั้น " เพราะแน่นอนว่าการมี IEnumerable ไม่ได้หยุดคุณใช้วิธีที่มีประสิทธิภาพมากขึ้นหากวัตถุมีอยู่และวิธีการขยาย LINQ to Objects ก็ทำได้อย่างแน่นอน ที่.
itowlson

17

IEnumerable เป็นอินเทอร์เฟซที่อนุญาตให้ทำการวนซ้ำผ่านคอลเลกชันของรายการต่างๆ

อาร์เรย์คือ. NET ที่อยู่ภายใน ถือสิ่งของประเภทเดียวกัน แต่มีขนาดคงที่ เมื่อคุณสร้างอาร์เรย์ด้วยองค์ประกอบ x แล้วจะไม่สามารถขยายหรือลดขนาดได้

IList กำหนดอินเทอร์เฟซสำหรับรายการและยังใช้ IEnumerable

รายการใช้อินเทอร์เฟซ IList; เป็นรายการที่เป็นรูปธรรม

ความแตกต่างระหว่างรายการ. NET และอาร์เรย์คือรายการสามารถเพิ่มองค์ประกอบเข้าไปได้ซึ่งจะมีขนาดใหญ่พอที่จะเก็บรายการที่ต้องการทั้งหมดได้ รายการจะเก็บสิ่งนี้ไว้ภายในอาร์เรย์และเมื่ออาร์เรย์ไม่ใหญ่พอที่จะเก็บองค์ประกอบทั้งหมดได้อีกต่อไปอาร์เรย์ใหม่จะถูกสร้างขึ้นและรายการจะถูกคัดลอก

IList & อาร์เรย์ทั้งสองใช้ IEnumerable นั่นเป็นวิธีการทำงานของอินเทอร์เฟซ - คลาสใช้สัญญาและปฏิบัติในลักษณะที่คล้ายกันและสามารถปฏิบัติในลักษณะเดียวกันได้ (คุณรู้ว่าคลาสนั้นใช้ IEnumerable คุณไม่จำเป็นต้องรู้วิธีการหรือวิธีการ) ฉันขอแนะนำให้คุณอ่านอินเทอร์เฟซและอื่น ๆ


1
การเพิ่มความคิดเห็นนี้สำหรับผู้อ่านที่ไม่ได้อ่านคำตอบทั้งหมด: อาร์เรย์และรายการ <> ทั้งสองใช้ IList <> (อาร์เรย์ใช้งานส่วนใหญ่อย่างชัดเจนน่าจะเป็นเพราะสมาชิกหลายคนโยน NotSupportedException) การบอกว่า IList สืบทอดมาจาก IE นั้นถูกต้องมากกว่าที่จะบอกว่ามันดำเนินการ
phoog

9

IEnumerable และ IList มีอินเตอร์เฟซ Array และ List เป็นคลาส อาร์เรย์ใช้ IEnumerable รายการใช้ IList ซึ่งขยาย IEnumerable

แก้ไข: ตามที่ itowlson กล่าวถึงในความคิดเห็น Array ยังใช้ IList


ฉันไม่! จะเกิดอะไรขึ้นถ้าคุณเริ่มเรียก IList.RemoveAt บนอาร์เรย์
Mark Simpson

มันพ่น NotSupportedException (หมายเหตุ IList.RemoveAt และวิธีการของ IList ที่คล้ายกันจะถูกนำไปใช้อย่างชัดเจนใน Array เพื่อไม่ให้แสดงในการอ้างอิง Array ปกติ!)
itowlson

1
@itowlson ฉันจะชี้ว่าเฉพาะ System.Array (คลาสต่อ se) เท่านั้นที่ใช้ IList แต่ T [] ไม่ใช้ IList <T> T [] ใช้ IEnumerable <T> แทน ฉันคิดว่า OP ไม่จำเป็นต้องขอ System.Array เมื่อขอ "Array" IMHO
usr-local-ΕΨΗΕΛΩΝ

6

การสร้างคอลเลคชัน IEnumerable นั้นขี้เกียจ ตัวอย่าง:

public IEnumerable<int> GetTwoInts()
{
  yield return 1;
  yield return 2;
}
public void Something()
{
  var twoInts = GetTwoInts();
}

ในเมธอด Something การเรียก GetTwoInts () จะไม่ส่งผลให้เมธอด GetTwoInts ถูกเรียกใช้งานจริงเนื่องจากการแจงนับจะไม่ถูกทำซ้ำ


ช่วยอธิบายให้ละเอียดหน่อยได้ไหม ฉันไม่แน่ใจว่าเข้าใจถูกหรือเปล่า คุณสามารถแสดงบรรทัดของรหัสในภายหลังซึ่งจะส่งผลให้วิธีการนั้นถูกเรียกใช้งานจริงหรือไม่? ขอบคุณ!
paaone

@paaone ใส่twoIntsในหรือโทรforeach twoInts.ToList()
row1

2

IEnumerableเป็นอินเตอร์เฟซที่ใช้งานทั่วไปที่ถูกใช้โดยเรียนเป็นจำนวนมากเช่นArray, ListและStringเพื่อที่จะให้คนย้ำกว่าคอลเลกชัน โดยพื้นฐานแล้วมันเป็นสิ่งที่ขับเคลื่อนforeachคำสั่ง

IListโดยทั่วไปเป็นวิธีที่คุณเปิดเผยตัวแปรประเภทListต่อผู้ใช้ปลายทาง อินเทอร์เฟซนี้อนุญาตให้เข้าถึงโดยสุ่มไปยังคอลเลกชัน


2

เพื่อเสริมคำตอบอื่น ๆ โปรดทราบว่ามีความแตกต่างด้านประสิทธิภาพระหว่างIList<T>และList<T>เมื่อเรียกใช้คำสั่ง foreach

นั่นเป็นเพราะออบเจ็กต์ตัววนซ้ำที่ส่งคืนโดยList<T>.GetEnumeratorเป็นชนิดค่าในขณะที่สิ่งที่ส่งคืนโดยIList<T>.GetEnumeratorเป็นชนิดการอ้างอิงดังนั้นจึงต้องมีการจัดสรรหน่วยความจำ (ดูตัวแจงนับประเภทค่าของรายการใน c # )

ในความคิดของฉันIList<T>ไม่ใช่อินเทอร์เฟซที่ดีมากอยู่ดี ตัวอย่างเช่นการโทรAddสามารถโยนได้ (ดูเหตุใดอาร์เรย์จึงใช้ IList ) หากคุณจำเป็นต้อง Encapsulation คุณต้องการจะดีกว่าการใช้หรือIEnumerable<T>IReadOnlyList<T>


1

นอกเหนือจากคำตอบอื่น ๆ แล้วการทำความเข้าใจความแตกต่างระหว่าง Enumerable vs List / Array เมื่อใช้ LINQ อาจส่งผลกระทบต่อประสิทธิภาพอย่างมาก ในระยะสั้น Enumerable สามารถรับรู้ได้ว่าเป็นตัวสร้างแบบสอบถามในขณะที่ List / Array เป็นผลลัพธ์ของแบบสอบถาม

ในบริบทของ LINQ เป็น SQL โดยใช้ EntityFramework เดิมเป็นเพียงการสร้างแบบสอบถาม SQL โดยไม่ดำเนินการกับฐานข้อมูลหรือโหลดข้อมูลใด ๆ ลงในหน่วยความจำในขณะที่ข้อมูลในภายหลังนั้นตรงกันข้าม นั่นเป็นเหตุผลที่เราจะเลื่อนการโทรออก.ToList()ไปจนกว่าเราจะต้องการมันในหน่วยความจำเพื่อใช้ตรรกะทางธุรกิจ

ในบริบทอื่นนิพจน์ LINQ ที่ส่งคืน IEnumerable จะเลื่อนการดำเนินการออกไปจนกว่า.ToList()จะถูกเรียก พิจารณาตัวอย่างด้านล่าง:

void Main()
{
    var w1 = "AB".AsEnumerable();
    Console.WriteLine($"W1: 1");
    w1 = w1.Where(W1);
    Console.WriteLine($"W1: 2");
    w1 = w1.Where(W2);
    Console.WriteLine($"W1: 3");
    w1.ToList();    
    Console.WriteLine($"----------");

    var w2 = "CD".AsEnumerable();
    Console.WriteLine($"W2: 1");
    w2 = w2.Where(W1);
    Console.WriteLine($"W2: 2");
    w2 = w2.ToList();
    Console.WriteLine($"W2: 3");
    w2 = w2.Where(W2);
    Console.WriteLine($"W2: 4");
    w2.ToList();    
    Console.WriteLine($"----------");
}

bool W1(char arg)
{
    Console.WriteLine($"W1:{arg}");
    return true;
}

bool W2(char arg)
{
    Console.WriteLine($"W2:{arg}");
    return true;
}

OUTPUT:
W1: 1
W1: 2
W1: 3
W1:A
W2:A
W1:B
W2:B
----------
W2: 1
W2: 2
W1:C
W1:D
W2: 3
W2: 4
W2:C
W2:D
----------

ในตัวอย่างแรกสองรายการ.Where()กำลัง "ต่อท้าย" และดำเนินการพร้อมกันในตอนท้ายเมื่อ.ToList()ถูกเรียกด้วยรายการ " A " ผ่านไปป์ก่อนจากนั้นรายการ " B " จึงเห็น " AABB " ในเอาต์พุตขณะที่ในตัวอย่างที่สอง.Where()จะดำเนินการทุกครั้งถ้าเราเรียก.ToList()ทันทีหลังจากนั้นจึงได้เห็น " ซีดี " และ " ซีดี " อีกครั้งเป็นครั้งที่สองเอาท์พุท ดังนั้นทุกครั้งที่ Enumerable ถูกแปลงเป็น List หรือ Array จะเสียค่าใช้จ่ายO (n)ซ้ำหนึ่งครั้งสำหรับไอเท็มทั้งหมดในคอลเล็กชันซึ่งจะมีผลกระทบต่อประสิทธิภาพเมื่อคอลเลกชันมีขนาดใหญ่

ถึงแม้ว่าเราจะไม่ได้มีแนวโน้มที่จะเขียนโค้ดด้วยวิธีนี้เรียก.ToList()ในระหว่างสาย LINQ แต่มันจะเกิดขึ้นบ่อยครั้งมากขึ้นเมื่อเราเข้าไปในรหัสปัจจัยวิธีนำมาใช้ใหม่ที่ส่งกลับมากกว่าList/ArrayIEnumerable

อย่างไรก็ตามนี่ไม่ได้หมายความว่าเราควรเปิดใช้งานเสมอIEnumerableไป ตามที่กล่าวไว้โดยTowlsonการดำเนินการเช่น.Count()จะทำให้เกิดการวนซ้ำบนคอลเลกชันในขณะที่รายการหรืออาร์เรย์จะมีข้อมูลนี้คำนวณไว้ล่วงหน้าดังนั้นการแปลงเป็นList/Arrayจะมีประสิทธิภาพมากขึ้นหากคุณวางแผนที่จะโทร.Count()หลายครั้ง นี่คือเหตุผลที่มี.Countคุณสมบัติในรายการแทนที่จะเป็น.Count()วิธีการนับ


0

นี่เป็นกระทู้เก่า แต่ยังคิดว่าจะตอบกลับ IEnumerable เป็นลักษณะการทำงานในขณะที่ Array เป็นโครงสร้างข้อมูล (การรวบรวมองค์ประกอบที่ต่อเนื่องกันที่มีขนาดคงที่ช่วยให้เข้าถึงองค์ประกอบโดยดัชนี) เมื่อ Array ใช้ IEnumerable มันควรจะแสดงคุณสมบัติที่มีอยู่โดยธรรมชาติของ IE ด้วย (เพื่ออำนวยความสะดวกในการทำซ้ำในคอลเลกชัน)

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