Когда использовать IList и когда использовать List

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

Ответ 1

Ниже следуют два правила:

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

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

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

Ответ 2

Руководства Microsoft, проверенные FxCop, препятствуют использованию List <T> в публичных API-интерфейсах - предпочитают 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;
    }
}

Ответ 3

Я согласен с рекомендацией Ли для принятия параметров, но не возвращаюсь.

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

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

Ответ 4

Там очень важно, что люди всегда не замечают:

Вы можете передать простой массив тому, что принимает параметр 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(), вы можете захотеть потребовать <<27 > вместо IList<T>.

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

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

public class Group {
  private IList<Person> people;

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

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

Ответ 9

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

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

ArrayList.Adapter(list).Sort()

Ответ 10

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

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

Еще одно отличие:  IList - это интерфейс и не может быть создан. Список - это класс и может быть создан. Это означает:

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

List<string> MyList = new List<string>

Ответ 11

В ситуациях, которые я обычно встречаю, я редко использую IList напрямую.

Обычно я просто использую его как аргумент метода

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

Это позволит мне выполнять общую обработку практически любого массива в платформе .NET, если только она не использует IEnumerable, а не IList, что иногда происходит.

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

Ответ 12

Вы должны использовать интерфейс только в том случае, если вам это нужно, например, если ваш список выбран в реализацию IList, отличную от List. Это верно, если, например, вы используете NHibernate, который бросает ILists в объект сумки NHibernate при извлечении данных.

Если List - единственная реализация, которую вы когда-либо использовали для определенной коллекции, не стесняйтесь объявлять ее как конкретную реализацию List.