簡體   English   中英

IEnumerable 和 Array、IList 和 List 有什么區別?

[英]What's the difference between IEnumerable and Array, IList and List?

IEnumerableArray什么區別?

IListList什么區別?

這些似乎具有相同的功能。

IEnumerable 僅提供最小的“可迭代”功能。 您可以遍歷序列,但僅此而已。 這有缺點——例如,使用 IEnumerable 計算元素或獲取第 n 個元素的效率非常低——但它也有優點——例如,IEnumerable 可能是一個無限序列,如素數序列。

Array 是一個固定大小的集合,可以隨機訪問(即您可以對其進行索引)。

List 是一個可變大小的集合(即您可以添加和刪除元素),可以隨機訪問。

IList 是一個接口,它將列表功能(計數、添加、刪除、索引器訪問)從各種具體類(如 List、BindingList、ObservableCollection 等)中抽象出來。

IEnumerable 是一個允許迭代項目集合的接口(例如,通過 foreach 關鍵字)。

數組是 .NET 內在函數。 它包含相同類型的項目,但具有固定大小。 一旦你創建了一個包含 x 個元素的數組,它就不能增長或縮小。

IList 定義了列表的接口,並且還實現了 IEnumerable。

List 實現了 IList 接口; 它是一種具體的列表類型。

.NET 列表和數組之間的區別在於列表可以添加元素——它們會增長到足以容納所有需要的項目。 該列表將其內部存儲在一個數組中,當數組不再足夠容納所有元素時,將創建一個新數組並復制項目。

IList 和數組都實現了 IEnumerable。 這就是接口的工作方式——類實現契約並以類似的方式運行,因此可以類似地對待(你知道類實現了 IEnumerable,你不需要知道如何或為什么)。 我建議你閱讀接口等。

IEnumerable 和 IList 是接口 Array 和 List 是類。 數組實現 IEnumerable。 List 實現了擴展 IEnumerable 的 IList。

編輯:正如 itowlson 在評論中提到的,Array 也實現了 IList。

IEnumerable 集合的生成是惰性的。 例子:

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

在 Some 方法中,對 GetTwoInts() 的調用實際上不會導致方法 GetTwoInts 被執行,因為枚舉永遠不會被迭代。

為了補充其他答案,請注意在執行 foreach 語句時IList<T>List<T>之間存在性能差異

這是因為返回的迭代器對象List<T>.GetEnumerator是數值類型而返回的一個IList<T>.GetEnumerator是引用類型,因此需要的存儲器分配(見在值類型列表中的枚舉C# )。

在我看來, IList<T>無論如何都不是一個很好的接口。 例如,調用Add可以拋出(請參閱為什么數組實現 IList? )。 如果您需要封裝,最好使用IEnumerable<T>IReadOnlyList<T>

IEnumerable是一個通用接口,被許多類使用,例如ArrayListString ,以便讓某人迭代集合。 基本上,這就是驅動foreach語句的原因。

IList通常是您向最終用戶公開List類型變量的方式。 該接口允許隨機訪問底層集合。

除了其他答案之外,在使用 LINQ 時了解 Enumerable 與 List/Array 之間的差異會對性能產生巨大影響。 簡而言之,Enumerable 可以被視為查詢構建器,而 List/Array 是查詢的結果。

在使用 EntityFramework 的 LINQ to SQL 上下文中,前者只是構建 SQL 查詢,而不對數據庫執行它,也不將任何數據加載到內存中,而后者則相反。 這就是為什么我們會推遲調用.ToList()直到我們在內存中需要它來執行業務邏輯。

在其他上下文中,返回 IEnumerable 的 LINQ 表達式將推遲執行,直到.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 ”中看到“ AABB ”輸出,而在第二個示例中,如果我們在之后立即調用.ToList() ,則每次都會執行.Where() ,因此再次看到“ CD ”然后“ CD ”,輸出兩次。 因此,每次將 Enumerable 轉換為 List 或 Array 時,都會對集合中的所有項目進行一次O(n)迭代,這會在集合較大時對性能產生影響。

雖然我們不.ToList()以這種方式編寫代碼,在 LINQ 調用之間調用.ToList()但當我們將代碼分解為返回List/Array而不是IEnumerable可重用方法時,這種情況會更頻繁地發生。

然而,這並不意味着我們應該始終對IEnumerable進行操作。 正如towlson所提到的,諸如.Count()將導致對集合的迭代,而 List 或 Array 將預先計算此信息,因此如果您計划調用.Count()則轉換為List/Array會更有效.Count()多次。 這也是為什么在 List 上有一個.Count屬性而不是一個.Count()方法來計算它的原因。

這是一個舊帖子,但仍然想回復。 IEnumerable 是一種行為,而 Array 是一種數據結構(具有固定大小的元素的連續集合,便於通過索引訪問元素)。 當數組實現 IEnumerable 時,它​​也應該描述 IEnumerable 固有屬性(促進集合迭代)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM