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


当前回答

我有两条原则:

接受最基本的工作类型 返回用户需要的最丰富的类型

因此,当编写一个接受集合的函数或方法时,不要让它接受List,而是让它接受IList<T>、ICollection<T>或IEnumerable<T>。泛型接口即使对于异构列表也仍然有效,因为System。Object也可以是T。如果您决定在以后使用Stack或其他数据结构,这样做将为您省去麻烦。如果在函数中所需要做的只是逐个遍历它,那么IEnumerable<T>才是真正需要的。

另一方面,当从函数返回一个对象时,您希望为用户提供尽可能丰富的操作集,而不需要他们进行强制转换。在这种情况下,如果内部是List<T>,返回一个List<T>的副本。

其他回答

你应该只在你需要它的时候使用这个接口,例如,如果你的列表被强制转换为一个IList实现而不是list。这是真的,例如,当你使用NHibernate,它在检索数据时将列表转换为NHibernate包对象。

如果List是您将用于某个集合的唯一实现,则可以将其声明为具体的List实现。

使用最低基类型总是最好的。这使接口的实现者或方法的使用者有机会在幕后使用他们喜欢的任何东西。

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

有一件重要的事情,人们似乎总是忽视:

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

未处理异常:系统。NotSupportedException:集合大小固定。

例如,考虑以下代码:

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

如果你像下面这样调用它,你会得到一个运行时异常:

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

这是因为使用带有IList<T>的普通数组违反了利斯科夫替换原则。

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

我不认为这类事情有严格的规则,但我通常会遵循使用尽可能轻松的方式的指导方针,直到绝对必要的时候。

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

public class Group {
  private IList<Person> people;

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

而且,如果你甚至不需要IList中的所有东西,你也可以使用IEnumerable。对于现代的编译器和处理器,我不认为它们真的有任何速度上的差异,所以这只是风格的问题。

由FxCop检查的微软指南不鼓励在公共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;
    }
}