Инициализируйте IEnumerable в новый список

#c# #list #linq #generics #ienumerable

#c# #Список #linq #общие #ienumerable ( количество )

Вопрос:

У меня есть такой класс, как этот:

 public class ItemList
{
    [JsonProperty("items")]
    public IEnumerable<Item> Items { get; set; }
}
 

И я хочу инициализировать пустой список, например, так:

 var newItemList = new ItemList
{
    Items = new List<Item>()
};
 

Но Items остается IEnumerable , и я не могу использовать Add или другие методы списка.

Комментарии:

1. необходимо ли использовать IEnumerable<Item> в классе? если это так, вам нужно добавить это свойство в список, в котором вы хотите использовать методы List .

2. Если теперь элементы всегда являются списком, вам следует изменить его на IList<Item> . В противном случае может быть причина, по которой это только IEnumerable .

3. Тип переменной / свойства (игнорирование dynamic ) определяет, какие методы доступны для вызова через эту переменную. Тип среды выполнения объекта, на который ссылаются, этого не делает.

4. @PMF IList не подходит, поскольку Add не всегда «доступен» IList , например, в массиве.

5. Итак, вы хотите, чтобы ваш класс имел общедоступное свойство типа IEnumerable<Item> , таким образом , чтобы любой мог обращаться с этим свойством так, как будто оно было a List<Item> ?

Ответ №1:

Почему бы не объявить его как список

 public class ItemList
{
    [JsonProperty("items")]
    public List<Item> Items { get; set; }
}
 

Список является IEnumerable расширением, поэтому имело бы смысл объявить его как список и иметь все методы на месте.

Поскольку вы уже объявили public IEnumerable<Item> Items { get; set; } , это не изменится.

Вы можете сделать это:

 Items = new List<Item>();
 

и даже

 ItemList itemlist = new itemlist();
itemlist.Items = new List<Item>();
((List<Item>)itemlist.Items).Add(new Item());
 

Но ((List<Item>)itemlist.Items).Add(new Item()); это небезопасно. Это будет работать при инициализации в виде списка, но это приведет к исключению во время выполнения, если вы попытаетесь выполнить приведение из какого-либо другого IEnumerable типа.

Ответ №2:

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

Вы должны преобразовать IEnumerable в List, если хотите использовать метод Add, реализованный классом List

 var myList = newItemList.ToList<T>();
myList.Add(new T());
 

где T — ваш конкретный класс.

Ответ №3:

Вы можете использовать любой из двух приведенных ниже вариантов.

  1. Вы можете использовать инициализатор списка, как показано ниже
      var itemList = new ItemList() {
         Items = new List<Item>() {
             new Item() { Name = "One" },
             new Item() { Name = "Two" }
         }
     };
     
  2. Вы можете полностью инициализировать список, а затем назначить его Items
  3. Гораздо лучшим подходом было бы использовать шаблон Builder, который помог бы в построении ItemList , как описано в https://code-maze.com/fluent-builder-recursive-generics /

Комментарии:

1. Однако я подумал о том же самом: «Я хочу инициализировать пустой список»

2. Вы можете инициализировать пустой список. Но, возможно, вы не сможете добавить к нему. Таким образом, вы можете написать метод в ItemList to AddItem , вам не нужно раскрывать детали реализации потребителям.

3. Вы можете IEnumerable предоставить доступ к внешнему миру на уровне экземпляра store a List и expose only Add метод.

Ответ №4:

Конечно, вы можете, просто разыграйте его:

 (Items as List<Item>).Add(someItem);
 

Возможно, будет довольно скучно постоянно его разыгрывать:

 public class ItemList
{
    private List<Item> _items - new List<Item>;
    [JsonProperty("items")]
    public IEnumerable<Item> Items { get => _items; private set => _items = value as List<string>; }
}
 

Затем внутри класса вы можете использовать _items .

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

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

Если вы собираетесь устанавливать список только один раз (никогда не меняйте его для нового экземпляра), например, в конструкторе, то вместо этого вы можете создать свойство только для чтения

Комментарии:

1. (Items as List<Item>)?.Add(someItem); если Items было что-то другое, чем список (не мой DV)

2. (Items as List<Item>).Add(someItem); Вполне может привести к исключениям с нулевыми ссылками. По крайней мере, вы должны добавить предупреждение в свой ответ.

3. Почему это должно быть что-то другое, чем список; OP принимает решение, что это будет

4. @AthanasiosKataras смотрите ту часть, где я рекомендую сделать установщик приватным. Этот вопрос касается исправления неправильного представления о том, что сохранение списка в ienumerable избавляет от метода Add