IEnumerable
和Array
有什么区别?
IList
和List
有什么区别?
它们似乎具有相同的功能。
IEnumerable
和Array
有什么区别?
IList
和List
有什么区别?
它们似乎具有相同的功能。
IEnumerable
仅提供了最基本的“可枚举”功能。您可以遍历序列,但仅止于此。
这有劣势;例如,使用 IEnumerable
计算元素数量或获取第 n 个元素非常低效。
但它也有优点;例如,IEnumerable
可以是无限序列,比如质数序列。
Array
是一个具有随机访问的固定大小的集合(即,您可以索引进入它)。
List
是一个具有随机访问和可变大小(即,您可以添加和删除元素)的集合。
IList
是一个接口,将列表功能(计数、添加、删除、索引器访问)与各种具体类(如 List
、BindingList
、ObservableCollection
等)分离。
IEnumerable是一个接口,允许遍历项的集合(例如使用foreach关键字)。
数组是.NET内置类型。它保存相同类型的项目,但其大小固定。一旦创建具有x元素的数组,它就不能增长或缩小。
IList定义列表的接口,并实现IEnumerable。
List实现了IList接口,它是一个具体的列表类型。
.NET列表和数组之间的区别在于,列表可以添加元素,它们会增长到足够大以容纳所有所需的项目。该列表在内部存储在数组中,当数组不再足够大以容纳所有元素时,将创建一个新数组并将项目复制过去。
IList和数组都实现IEnumerable。这就是接口的工作方式,类实现协定并表现出类似的行为,因此可以被类似地处理(您知道类实现IEnumerable,不需要知道具体细节)。建议您阅读关于接口等内容的相关资料。
IEnumerable集合的生成是延迟的。 示例:
public IEnumerable<int> GetTwoInts()
{
yield return 1;
yield return 2;
}
public void Something()
{
var twoInts = GetTwoInts();
}
在方法Something中,对GetTwoInts()的调用实际上不会导致执行GetTwoInts方法,因为该枚举从未被迭代。
twoInts
放入 foreach
循环中或调用 twoInts.ToList()
。 - row1IEnumerable和IList是接口。Array和List是类。Array实现了IEnumerable。List实现了继承自IEnumerable的IList。
编辑:如itowlson在评论中提到,Array也实现了IList。
IEnumerable
是一个通用的接口,被许多类(例如 Array
、List
和 String
)使用,以便让某人对集合进行迭代。基本上,它是驱动 foreach
语句的东西。
IList
是通常用于向最终用户公开类型为 List
的变量的方式。此接口允许随机访问底层集合。
IList<T>
和List<T>
之间存在性能差异。List<T>.GetEnumerator
返回的迭代器对象是值类型,而IList<T>.GetEnumerator
返回的迭代器对象是引用类型,因此需要进行内存分配(请参见Enumerator of value type of list in c#)。IList<T>
不是一个很好的接口。例如,调用Add
可能会抛出异常(请参见Why array implements IList?)。如果您需要封装,最好使用IEnumerable<T>
或IReadOnlyList<T>
。.ToList()
直到需要在内存中执行业务逻辑。.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",而在第二个示例中,如果我们立即调用 .ToList()
,则 .Where()
每次都会被执行,因此看到 "CD" 然后再次看到 "CD",输出两次。因此,每次将 Enumerable 转换为 List 或 Array 时,都会花费一个 O(n) 迭代来遍历集合中的所有项,当集合很大时,这将影响性能。.ToList()
,但当我们将代码分解为返回 List/Array
而不是 IEnumerable
的可重用方法时,这种情况会更频繁地发生。IEnumerable
。正如 towlson 所提到的那样,例如 .Count()
这样的操作将导致对集合进行迭代,而 List 或 Array 将具有预先计算的信息,因此如果您计划多次调用 .Count()
,则转换为 List/Array
将更有效率。这也是为什么 List 上有一个 .Count
属性来计数,而不是一个 .Count()
方法。这是一篇旧帖子,但仍然想回复。 IEnumerable 是一种行为,而 Array 是一种数据结构(连续的元素集合,具有固定大小,便于通过索引访问元素)。 当一个 Array 实现 IEnumerable 时,它应该也展示 IEnumerable 的内在属性(即便于迭代集合)。
IEnumerable
可能是惰性的,所以如果你的迭代器内部有业务逻辑,你必须小心处理:public IEnumerable<int> GetTwoInts()
{
Console.WriteLine("1");
yield return 1;
Console.WriteLine("2");
yield return 2;
}
public void Something()
{
var twoInts = GetTwoInts();
}
使用答案中的示例,只有在“使用”twoInts
时,即在foreach循环中使用或调用ToArray
时,才会调用两个Console.WriteLine
,这可能不直观。