#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>
, таким образом , чтобы любой мог обращаться с этим свойством так, как будто оно было aList<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:
Вы можете использовать любой из двух приведенных ниже вариантов.
- Вы можете использовать инициализатор списка, как показано ниже
var itemList = new ItemList() { Items = new List<Item>() { new Item() { Name = "One" }, new Item() { Name = "Two" } } };
- Вы можете полностью инициализировать список, а затем назначить его
Items
- Гораздо лучшим подходом было бы использовать шаблон Builder, который помог бы в построении
ItemList
, как описано в https://code-maze.com/fluent-builder-recursive-generics /
Комментарии:
1. Однако я подумал о том же самом: «Я хочу инициализировать пустой список»
2. Вы можете инициализировать пустой список. Но, возможно, вы не сможете добавить к нему. Таким образом, вы можете написать метод в
ItemList
toAddItem
, вам не нужно раскрывать детали реализации потребителям.3. Вы можете
IEnumerable
предоставить доступ к внешнему миру на уровне экземпляра store aList
и expose onlyAdd
метод.
Ответ №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