IEnumerable<T> 包含List<T> 内部的一小部分内容,其中包含与IEnumerable<T> 相同的内容,但更多!如果您想要一组较小的功能,您只能使用IEnumerable<T>。如果您打算使用更大、更丰富的功能集,请使用 List<T>。
比萨解释
这里有一个更全面的解释,说明在用 Microsoft C# 等 C 语言实例化对象时,为什么要使用 IEnumerable<T> 和 List<T> 之类的接口,反之亦然。
将IEnumerable<T> 和IList<T> 之类的接口视为比萨饼(意大利辣香肠、蘑菇、黑橄榄...)中的单个成分,以及具体类 List<T> 作为披萨。 List<T> 实际上是一个Supreme Pizza,它始终包含所有接口成分的组合(ICollection、IEnumerable、IList 等)。
就披萨及其浇头而言,您获得的内容取决于您在内存中创建其对象引用时“键入”列表的方式。您必须按如下方式声明您正在烹饪的比萨饼类型:
// Pepperoni Pizza: This gives you a single Interface's members,
// or a pizza with one topping because List<T> is limited to acting like an IEnumerable<T> type.
IEnumerable<string> pepperoniPizza = new List<string>();
// Supreme Pizza: This gives you access to ALL 8 Interface members combined
// or a pizza with ALL the ingredients because List type uses all Interfaces!!
List<string> supremePizza = new List<string>();
请注意,您不能将接口实例化为自身(或吃生的意大利辣香肠)。当您将List<T> 实例化为一种接口类型或IEnumerable<T> 时,您只能访问其实现并获得带有一个浇头的意大利辣香肠披萨。您只能访问IEnumerable<T> 成员,而无法查看List<T> 中的所有其他接口成员。当List<T> 被实例化为List<T> 时,它实现了所有8 个接口,因此它可以访问它已实现的所有接口的所有成员(或Supreme Pizza toppings)!
这是List<T> 类,向您展示WHY。请注意 .NET 库中的 List<T> 已实现所有其他接口!注意IEnumerable<T> 只是它实现的所有接口成员的一小部分。
public class List<T> :
ICollection<T>,
IEnumerable<T>,
IEnumerable,
IList<T>,
IReadOnlyCollection<T>,
IReadOnlyList<T>,
ICollection,
IList
{
public List();
public List(IEnumerable<T> collection);
public List(int capacity);
public T this[int index] { get; set; }
public int Count { get; }
public int Capacity { get; set; }
public void Add(T item);
public void AddRange(IEnumerable<T> collection);
public ReadOnlyCollection<T> AsReadOnly();
public bool Exists(Predicate<T> match);
public T Find(Predicate<T> match);
public void ForEach(Action<T> action);
public void RemoveAt(int index);
public void Sort(Comparison<T> comparison);
// ......and much more....
}
那么为什么不始终将List<T> 实例化为List<T>?
将List<T> 实例化为List<T> 可让您访问所有接口成员! 但您可能不需要所有内容。选择一种接口类型允许您的应用程序存储具有较少成员的较小对象并保持您的应用程序紧凑。谁每次都需要Supreme Pizza?
但使用接口类型还有一个第二个原因:灵活性。因为 .NET 中的其他类型,包括您自己的自定义类型,可能使用相同的“流行”接口类型,这意味着您可以稍后将您的 List<T> 类型替换为任何其他实现的类型,例如 IEnumerable<T>。如果您的变量是一个接口类型,您现在可以切换出使用List<T> 以外的其他对象创建的对象。依赖注入是使用接口而不是具体类型的这种灵活性的一个很好的例子,以及为什么您可能希望使用接口创建对象。