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

Ris*_*smo 174 .net c#

我知道IList是接口,List是具体类型,但我仍然不知道何时使用每一个.我现在正在做的是如果我不需要使用该接口的Sort或FindAll方法.我对吗?有没有更好的方法来决定何时使用界面或具体类型?

Lee*_*Lee 166

我遵循两条规则:

  • 接受最有效的基本类型
  • 返回您的用户需要的最丰富的类型

因此,在编写采用集合的函数或方法时,不要将其写入List,而是写入IList <T>,ICollection <T>或IEnumerable <T>.即使对于异构列表,通用接口仍然可以工作,因为System.Object也可以是T.如果您决定使用堆栈或其他一些数据结构,这样做可以帮助您避免头痛.如果您需要在函数中完成所有操作,那么IEnumerable <T>就是您应该要求的全部内容.

另一方面,当从函数返回一个对象时,您希望为用户提供最丰富的操作集,而无需他们进行转换.因此,在这种情况下,如果它在内部是List <T>,则将副本作为List <T>返回.

  • 您不应该以任何不同的方式处理输入/输出类型.输入和输出类型应该*都是*支持客户需求的最基本类型(最好是接口).封装依赖于尽可能少地告诉客户类的实现.如果返回一个具体的List,则不能在不强制所有客户端重新编译/更新的情况下更改为其他更好的类型. (43认同)
  • 我不同意这两个规则......在这种情况下返回IList(更好的IEnumarable)时我会使用最原始的类型和specialy,你应该在你的函数里面使用List.然后,当您需要"添加"或"排序"时,如果需要更多,则使用Collection,然后使用List.所以我的硬性规则是:START始终使用IENumarable,如果你需要更多,那么扩展...... (11认同)
  • 我非常强烈反对Point#2,特别是如果它位于service/api边界.返回可修改的集合可以给人一种集合"实时"的印象,并且调用诸如`Add()`和`Remove()`之类的方法可能会产生超出集合的效果.返回诸如"IEnumerable"之类的只读接口通常是采用数据检索方法的方法.您的消费者可以根据需要将其投影为更丰富的类型. (6认同)
  • 为方便起见,"两条规则"有一个名称:[健全性原则(又名Postel定律)](https://en.wikipedia.org/wiki/Robustness_principle). (2认同)

Joe*_*Joe 55

由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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我最喜欢这个解释/例子! (3认同)

Mat*_*son 26

人们总是忽视一件重要的事情:

您可以将一个普通数组传递给接受IList<T>参数的东西,然后您可以调用IList.Add()并接收运行时异常:

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

例如,请考虑以下代码:

private void test(IList<int> list)
{
    list.Add(1);
}
Run Code Online (Sandbox Code Playgroud)

如果按如下方式调用,则会出现运行时异常:

int[] array = new int[0];
test(array);
Run Code Online (Sandbox Code Playgroud)

发生这种情况是因为使用IList<T>违反Liskov替换原则的普通数组.

因此,如果您打电话,IList<T>.Add()您可能需要考虑要求List<T>而不是IList<T>.

  • 对于每个接口来说都是如此。如果您想坚持您的论点,那么您可以争论根本不使用任何接口,因为它的某些实现可能会抛出异常。另一方面,如果您考虑 OP 给出的建议,更喜欢 `List&lt;T&gt;` 而不是 `IList&lt;T&gt;`,那么您还应该了解推荐 `IList&lt;T&gt;` 的原因。(例如 https://blogs.msdn.microsoft.com/kcwalina/2005/09/26/why-we-dont-recommend-using-listt-in-public-apis/) (2认同)
  • @MichaWiedenmann我的回答是针对您调用`IList &lt;T&gt; .Add()`时的情况。我并不是说您*不应*使用`IList &lt;T&gt;`-我只是指出可能的陷阱。(如果可以的话,我倾向于优先使用`IEnumerable &lt;T&gt;`或`IReadOnlyList &lt;T&gt;`或`IReadOnlyCollection &lt;T&gt;`而不是`IList &lt;T&gt;`。) (2认同)

ICR*_*ICR 24

我同意李的建议,即参加参赛,但不回复.

如果指定返回接口的方法,则意味着您可以在以后更改确切的实现,而无需消费方法.我以为我永远不需要从List <T>更改,但后来必须更改为使用自定义列表库来提供它提供的额外功能.因为我只返回了一个IList <T>使用该库的人不得不改变他们的代码.

当然,只需要应用于外部可见的方法(即公共方法).我个人甚至在内部代码中使用接口,但是如果你进行重大更改,你可以自己更改所有代码,但这并不是绝对必要的.


小智 18

IEnumerable 你应该尝试使用适合你目的的最不具体的类型.IEnumerable不如IList特定,当您想循环遍历集合中的项目时,可以使用IEnumerable

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

列表 列表实现IList

  • 优秀,清晰的答案,我认为是有帮助的.但是,我想补充一点,对于大多数开发人员来说,大多数情况下,程序大小和性能的微小差异并不值得担心:如果有疑问,只需使用List. (2认同)

tgm*_*dbm 9

最好尽可能使用最低的基本类型.这使您的界面的实现者或您的方法的消费者有机会在幕后使用他们喜欢的任何东西.

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


Mar*_*ade 5

如果您在单个方法中工作(或者在某些情况下甚至在单个类或程序集中)并且外面没有人会看到您正在做什么,请使用List的完整性.但是,如果您正在与外部代码交互,例如当您从方法返回列表时,那么您只需要声明接口而不必将自己绑定到特定实现,特别是如果您无法控制谁编译您的之后的代码.如果你开始使用具体的类型并且你决定改为另一个,即使它使用相同的接口,你也会打破其他人的代码,除非你开始使用接口或抽象基类型.