Когда использовать IList и когда использовать List - PullRequest
171 голосов
/ 20 августа 2008

Я знаю, что IList - это интерфейс, а List - конкретный тип, но я до сих пор не знаю, когда использовать каждый из них. Что я делаю сейчас, если мне не нужны методы Sort или FindAll, я использую интерфейс. Я прав? Есть ли лучший способ решить, когда использовать интерфейс или конкретный тип?

Ответы [ 12 ]

162 голосов
/ 20 августа 2008

Есть два правила, которым я следую:

  • Примите самый базовый тип, который будет работать
  • Верните самый богатый тип, который понадобится вашему пользователю

Таким образом, при написании функции или метода, который принимает коллекцию, напишите ее не для получения List, а IList , ICollection или IEnumerable . Универсальные интерфейсы по-прежнему будут работать даже для разнородных списков, поскольку System.Object также может быть буквой T. Это избавит вас от головной боли, если вы решите использовать стек или другую структуру данных в будущем. Если все, что вам нужно сделать в функции - это foreach, то IEnumerable - это действительно все, о чем вы должны просить.

С другой стороны, когда вы возвращаете объект из функции, вы хотите предоставить пользователю максимально богатый набор операций без необходимости его использования. Так что в этом случае, если это внутренний список , верните копию в виде списка .

54 голосов
/ 17 сентября 2008

Рекомендации Microsoft, проверенные FxCop, не рекомендуют использовать List в общедоступных API - предпочтительнее IList .

Кстати, теперь я почти всегда объявляю одномерные массивы как IList , что означает, что я могу последовательно использовать свойство IList .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;
    }
}
25 голосов
/ 30 октября 2013

Есть важная вещь, которую люди, кажется, всегда упускают из виду:

Вы можете передать простой массив чему-то, что принимает параметр IList<T>, а затем вы можете вызвать IList.Add() и получить исключение времени выполнения:

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

Например, рассмотрим следующий код:

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>.

24 голосов
/ 20 августа 2008

Я бы согласился с советом Ли о том, чтобы принимать параметры, но не возвращать.

Если вы указываете свои методы для возврата интерфейса, это означает, что вы можете изменить точную реализацию позже, даже не узнав о потребляющем методе. Я думал, что мне никогда не понадобится переходить из List , но позже пришлось изменить его, чтобы использовать настраиваемую библиотеку списков для дополнительной функциональности, которую она предоставляла. Поскольку я только возвратил IList , ни один из людей, которые использовали библиотеку, не должен был менять свой код.

Конечно, это нужно применять только к методам, которые являются внешне видимыми (то есть общедоступными методами). Я лично использую интерфейсы даже во внутреннем коде, но, поскольку вы можете изменить весь код самостоятельно, если вносите критические изменения, это не является строго необходимым.

17 голосов
/ 20 июля 2013

IEnumerable Вы должны попытаться использовать наименее определенный тип, который соответствует вашим целям. IEnumerable менее специфичен, чем IList Вы используете IEnumerable, когда хотите перебрать элементы коллекции

IList IList реализует IEnumerable Вы должны использовать IList, когда вам нужен доступ по индексу к вашей коллекции, добавление и удаление элементов и т. Д.

Список Список реализует IList

9 голосов
/ 20 августа 2008

Всегда лучше использовать минимально возможный базовый тип. Это дает разработчику вашего интерфейса или потребителю вашего метода возможность использовать все что угодно за кулисами.

Для коллекций вы должны стремиться использовать IEnumerable, где это возможно. Это дает большую гибкость, но не всегда подходит.

5 голосов
/ 20 августа 2008

Если вы работаете в одном методе (или даже в одном классе или сборке в некоторых случаях), и никто за пределами не увидит, что вы делаете, используйте полноту списка. Но если вы взаимодействуете с внешним кодом, как, например, когда вы возвращаете список из метода, то вам нужно только объявить интерфейс, не привязывая себя к конкретной реализации, особенно если вы не можете контролировать, кто компилирует против вашего код потом. Если вы начали с конкретного типа и решили заменить его на другой, даже если он использует тот же интерфейс, вы нарушите чужой код, если только вы не начали с интерфейса или абстрактного базового типа.

4 голосов
/ 17 сентября 2008

Чаще всего вам лучше использовать наиболее общий используемый тип, в данном случае IList или даже лучше интерфейс IEnumerable, так что вы можете удобно переключать реализацию в более позднее время.

Однако в .NET 2.0 есть досадная вещь - в IList нет метода Sort () . Вместо этого вы можете использовать прилагаемый адаптер:

ArrayList.Adapter(list).Sort()
4 голосов
/ 20 августа 2008

Я не думаю, что существуют жесткие и быстрые правила для такого рода вещей, но я обычно придерживаюсь принципа использования как можно более легкого пути, пока это не станет абсолютно необходимым.

Например, допустим, у вас есть класс Person и класс Group. В экземпляре Group много людей, поэтому список здесь имеет смысл. Когда я объявляю объект списка в Group, я буду использовать IList<Person> и создавать его как List.

public class Group {
  private IList<Person> people;

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

И, если вам даже не нужно все в IList, вы всегда можете также использовать IEnumerable. С современными компиляторами и процессорами, я не думаю, что есть какая-то разница в скорости, так что это больше вопрос стиля.

2 голосов
/ 19 августа 2013

Объект AList позволяет создавать список, добавлять в него объекты, удалять его, обновлять, индексировать в него и т. Д. Список используется всякий раз, когда вам нужен просто общий список, в котором вы указываете тип объекта и все.

IList, с другой стороны, является интерфейсом. По сути, если вы хотите создать свой собственный тип List, скажем, класс списка с именем BookList, то вы можете использовать интерфейс, чтобы дать вам базовые методы и структуру для вашего нового класса. IList предназначен для случаев, когда вы хотите создать свой собственный специальный подкласс, который реализует List.

Другое отличие: IList является интерфейсом и не может быть создан. Список является классом и может быть создан. Это значит:

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

List<string> MyList = new List<string>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...