C# 什么';IEnumerable和Array、IList和List之间的区别是什么?
C# 什么';IEnumerable和Array、IList和List之间的区别是什么?,c#,C#,IEnumerable和Array之间有什么区别 IList和List之间有什么区别 它们似乎具有相同的功能。IEnumerable和IList是。数组和列表是类。数组实现IEnumerable。列表实现了扩展IEnumerable的IList 编辑:正如itowlson在一篇评论中提到的,数组还实现了IList。IEnumerable是一个接口,允许通过项目集合进行迭代(例如,通过foreach关键字) 数组是.NET固有的。它保存相同类型的项目,但大小固定。一旦创建了包含x个元素的数组,它就
IEnumerable
和Array
之间有什么区别
IList
和List
之间有什么区别
它们似乎具有相同的功能。IEnumerable和IList是。数组和列表是类。数组实现IEnumerable。列表实现了扩展IEnumerable的IList
编辑:正如itowlson在一篇评论中提到的,数组还实现了IList。IEnumerable是一个接口,允许通过项目集合进行迭代(例如,通过foreach关键字) 数组是.NET固有的。它保存相同类型的项目,但大小固定。一旦创建了包含x个元素的数组,它就无法增长或收缩 IList定义了列表的接口,还实现了IEnumerable 列表实现了IList接口;它是一种具体的列表类型 NET列表和数组之间的区别在于列表可以添加元素——它们的大小足以容纳所有必需的项。该列表将其内部存储在一个数组中,当该数组不再大到足以容纳所有元素时,将创建一个新数组,并跨该数组复制项
IList和数组都实现IEnumerable。接口就是这样工作的——类实现了契约并以类似的方式运行,因此可以被类似地对待(您知道类实现了IEnumerable,您不需要知道如何或为什么)。我建议您仔细阅读接口等方面的内容。IEnumerable只提供最低限度的“iterable”功能。您可以遍历序列,但仅此而已。这有缺点——例如,使用IEnumerable计算元素或获取第n个元素的效率非常低——但它也有优点——例如,IEnumerable可以是一个无止境的序列,就像素数序列一样 数组是一个具有随机访问的固定大小集合(即,您可以对其进行索引) 列表是一个具有随机访问权限的可变大小集合(即,您可以添加和删除元素)
IList是一个接口,它将列表功能(计数、添加、删除、索引器访问)从各种具体类(如list、BindingList、ObservableCollection等)中抽象出来。
IEnumerable
是一个通用接口,被许多类使用,例如数组
,列表
和字符串
,以便有人在集合上迭代。基本上,它是驱动foreach
语句的因素
IList
通常是向最终用户公开List
类型变量的方式。此接口允许对基础集合进行随机访问。生成IEnumerable集合是惰性的。
例如:
public IEnumerable GetTwoInts()
{
收益率1;
收益率2;
}
公之于众
{
var twoInts=GetTwoInts();
}
在方法中,对GetTwoInts()的调用实际上不会导致执行方法GetTwoInts,因为枚举从未被迭代过。这是一篇老文章,但仍然考虑回复。 IEnumerable是一种行为,而Array是一种数据结构(具有固定大小的元素的连续集合,便于通过索引访问元素)。
当数组实现IEnumerable时,它还应该描述IEnumerable的固有属性(促进对集合的迭代)。为了补充其他答案,请注意,在执行foreach语句时,
IList
和List
之间存在性能差异
这是因为List.GetEnumerator
返回的迭代器对象是值类型,而IList.GetEnumerator
返回的迭代器对象是引用类型,因此需要内存分配(请参阅)
在我看来,
IList
无论如何都不是一个很好的界面。例如,调用Add
可以抛出(请参阅)。如果您需要封装,最好使用IEnumerable
或IReadOnlyList
除了其他答案之外,了解使用LINQ时可枚举与列表/数组之间的差异会对性能产生巨大影响。简而言之,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”,而在第二个示例中,.Where()如果我们随后立即调用.ToList()
,则每次都会执行,因此会看到“CD”,然后再次看到“CD”,并输出两次。因此,每次将可枚举项转换为列表或数组时,都会对集合中的所有项进行一次迭代,这在集合较大时会对性能产生影响
虽然我们不倾向于以这种方式编写代码,在LINQ调用之间调用.ToList()
,但当我们将代码分解为返回List/Array
而不是IEnumerable
的可重用方法时,这种情况会发生得更频繁
然而,这并不意味着我们应该总是对IEnumerable
进行操作。作为m
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
----------