何时使用IList以及何时使用List

我知道IList是接口,List是具体types,但我仍然不知道什么时候使用每一个。 我现在正在做的是如果我不需要使用接口的Sort或FindAll方法。 我对吗? 有更好的方法来决定何时使用接口或具体types?

我遵循两条规则:

  • 接受最基本的工作types
  • 返回您的用户将需要的最丰富的types

因此,在编写一个接收集合的函数或方法时,不要写一个List,而要写一个IList <T>,ICollection <T>或IEnumerable <T>。 即使对于异构列表,通用接口仍然可以工作,因为System.Object也可以是T。 如果您决定在路上继续使用堆栈或其他数据结构,这样做可以节省您的头痛。 如果你所需要做的只是通过foreach,那么IEnumerable <T>实际上就是你应该要求的。

另一方面,当从一个函数中返回一个对象时,你希望给用户尽可能多的操作,而不必四处乱跑。 所以在这种情况下,如果它是一个List <T>,则返回一个List <T>的副本。

通过FxCop检查的Microsoft指导方针阻止在公共API中使用List <T> – 更喜欢IList <T>。

顺便说一下,我现在几乎总是将一维数组声明为IList <T>,这意味着我可以一致地使用IList <T> .Count属性而不是Array.Length。 例如:

 public interface IMyApi { IList<int> GetReadOnlyValues(); } public class MyApiImplementation : IMyApi { public IList<int> GetReadOnlyValues() { List<int> myList = new List<int>(); ... populate list return myList.AsReadOnly(); } } public class MyMockApiImplementationForUnitTests : IMyApi { public IList<int> GetReadOnlyValues() { IList<int> testValues = new int[] { 1, 2, 3 }; return testValues; } } 

我会同意李的build议,但不能返回。

如果你指定你的方法来返回一个接口,这意味着你可以随时改变确切的实现,而不需要消费方法。 我以为我永远不需要从一个List <T>改变,但不得不稍后更改为使用自定义列表库来提供额外的function。 因为我只返回一个IList <T>所有使用这个库的人都不得不改变他们的代码。

当然,这只需要适用于外部可见的方法(即公共方法)。 即使在内部代码中我也亲自使用接口,但是如果您进行重大更改,则可以自行更改所有代码,这不是必须的。

人们似乎忽略了一件重要的事情:

您可以传递一个普通数组来接受一个IList<T>参数,然后您可以调用IList.Add()并将收到一个运行时exception:

Unhandled Exception: System.NotSupportedException: Collection was of a fixed size.

例如,请考虑以下代码:

 private void test(IList<int> list) { list.Add(1); } 

如果您按如下所示调用它,则会得到一个运行时exception:

 int[] array = new int[0]; test(array); 

发生这种情况是因为使用IList<T>普通数组违反了Liskovreplace原则。

因此,如果你调用IList<T>.Add()你可能需要考虑需要List<T>而不是IList<T>

IEnumerable你应该尝试使用最适合你的目的的具体types。 IEnumerable不如IList特定于您想循环访问集合中的项目时使用IEnumerable

IList IList实现IEnumerable当需要通过索引访问您的集合,添加和删除元素等时,您应该使用IList。

列表列表实现IList

总是最好使用可能的最低基本types。 这使得你的接口的实现者或者你的方法的消费者有机会在幕后使用他们喜欢的任何东西。

对于集合,您应该尽可能使用IEnumerable。 这给了最大的灵活性,但并不总是适合的。

如果你在一个单一的方法(甚至在某些情况下,在一个单一的类或程序集)内工作,外面没有人会看到你在做什么,请使用List的完整性。 但是,如果您正在与外部代码进行交互,就像您从方法返回列表一样,那么您只需要声明界面,而不必将自己绑定到特定的实现上,特别是如果您无法控制针对您编译的对象之后的代码。 如果你从一个具体的types开始,并决定改变到另一个types,即使它使用了相同的接口,你将打破别人的代码,除非你开始使用接口或抽象基types。

我不认为这种事情有严格的规定,但是我通常会按照最轻的方法去做,直到绝对必要的时候。

例如,假设您有一个Person类和一个Group类。 一个Group实例有很多人,所以这里的List是有道理的。 当我在Group声明列表对象时,将使用一个IList<Person>并将其实例化为一个List

 public class Group { private IList<Person> people; public Group() { this.people = new List<Person>(); } } 

而且,如果你甚至不需要IList所有东西,你总是可以使用IEnumerable 。 使用现代编译器和处理器,我不认为有真正的速度差异,所以这只是一个风格问题。

你通常最好使用最通用的可用types,在这种情况下是IList,甚至更好的IEnumerable接口,以便以后可以方便地切换实现。

但是,在.NET 2.0中,有一个恼人的事情 – IList没有一个Sort()方法。 您可以使用提供的适配器来代替:

 ArrayList.Adapter(list).Sort() 

AList对象允许你创build一个列表,向它添加东西,删除它,更新它,编入索引等等。只要你需要一个通用列表,就可以使用列表,只要你指定对象types就可以了。

IList另一方面是一个接口。 基本上,如果你想创build自己的Listtypes,比如说一个名为BookList的列表类,那么你可以使用Interface为你的新类提供基本的方法和结构。 IList是当你想创build自己的,实现列表的特殊子类。

另一个区别是:IList是一个接口,不能被实例化。 列表是一个类,可以实例化。 它的意思是:

 IList<string> MyList = new IList<string>(); List<string> MyList = new List<string> 

在我经常遇到的情况下,我很less直接使用IList。

通常我只是用它作为方法的参数

 void ProcessArrayData(IList almostAnyTypeOfArray) { // Do some stuff with the IList array } 

这将允许我在.NET框架中的几乎任何数组上进行通用处理,除非它使用IEnumerable而不是IList,有时会发生这种情况。

这真的归结为你需要的function。 我build议在大多数情况下使用List类。 当您需要制作一个自定义数组时,IList最适合您,这个自定义数组可以包含一些您想要封装在集合中的特定规则,所以您不必重复自己,但仍然希望.NET将其识别为列表。

只有在需要的时候才应该使用这个接口,例如,如果你的列表被转换成List之外的IList实现。 例如,当你使用NHibernate,在检索数据时将IList强制转换成一个NHibernate包对象时,就是这样。

如果List是您将用于特定集合的唯一实现,请随意将其声明为具体的List实现。