Лучше ли использовать Enumerable.Empty <T>() в отличие от нового List <T>() для инициализации IEnumerable <T>?
Предположим, что у вас есть класс Person:
public class Person
{
public string Name { get; set;}
public IEnumerable<Role> Roles {get; set;}
}
Я должен, очевидно, создать экземпляр ролей в конструкторе.
Теперь я использовал это со списком следующим образом:
public Person()
{
Roles = new List<Role>();
}
Но я обнаружил этот статический метод в пространстве имен System.Linq
IEnumerable<T> Enumerable.Empty<T>();
Из MSDN:
Метод Empty(TResult)()
кэширует пустая последовательность типа TResult
. когда возвращаемый им объект перечисляется, он не дает элементов.
В некоторых случаях этот метод полезен для передачи пустой последовательности пользовательский метод, который принимает IEnumerable(T)
. Его также можно использовать для генерировать нейтральный элемент для методов таких как Union
. См. Раздел "Пример" для примера этого использования
Так лучше написать такой конструктор? Вы используете его? Зачем? или если нет, то почему?
public Person()
{
Roles = Enumerable.Empty<Role>();
}
Ответы
Ответ 1
Я думаю, что большинство сообщений пропустили главное. Даже если вы используете пустой массив или пустой список, это объекты и они хранятся в памяти. Чем сборщик мусора должен заботиться о них. Если вы имеете дело с высокопроизводительным приложением, это может заметно повлиять.
Enumerable.Empty
не создает объект за вызов, тем самым уменьшая нагрузку на GC.
Если код находится в месте с низкой пропускной способностью, то он сводится к эстетическим соображениям.
Ответ 2
Я думаю, что Enumerable.Empty<T>
лучше, потому что он более явный: ваш код четко указывает ваши намерения. Это также может быть немного более эффективным, но это только второстепенное преимущество.
Ответ 3
Предполагая, что вы действительно хотите заполнить свойство Roles
каким-либо образом, затем инкапсулируйте его, сделав его setter private и инициализируя его новым списком в конструкторе:
public class Person
{
public string Name { get; set; }
public IList<Role> Roles { get; private set; }
public Person()
{
Roles = new List<Role>();
}
}
Если вы действительно хотите иметь публичный сеттер, оставьте Roles
со значением null
и избежите размещения объекта.
Ответ 4
В представлении производительности давайте посмотрим, как реализуется Enumerable.Empty<T>
.
Он возвращает EmptyEnumerable<T>.Instance
, который определяется как:
internal class EmptyEnumerable<T>
{
public static readonly T[] Instance = new T[0];
}
Статические поля для общих типов выделяются для одного типового параметра. Это означает, что среда выполнения может лениво создавать эти пустые массивы только для потребностей кода пользователя и повторно использовать экземпляры столько раз, сколько необходимо, не добавляя никакого давления на сборщик мусора.
В частности:
Debug.Assert(ReferenceEquals(Enumerable.Empty<int>(), Enumerable.Empty<int>()));
Ответ 5
Проблема с вашим подходом заключается в том, что вы не можете добавлять какие-либо элементы в коллекцию - у меня будет такая приватная структура, как список, а затем выставлять элементы как перечисляемые:
public class Person
{
private IList<Role> _roles;
public Person()
{
this._roles = new List<Role>();
}
public string Name { get; set; }
public void AddRole(Role role)
{
//implementation
}
public IEnumerable<Role> Roles
{
get { return this._roles.AsEnumerable(); }
}
}
Если вы хотите, чтобы какой-то другой класс создал список ролей (который я бы не рекомендовал), я бы не инициализировал перечислимое вообще в Person.
Ответ 6
Типичная проблема с раскрытием частного списка как IEnumerable заключается в том, что клиент вашего класса может испортить его путем литья. Этот код будет работать:
var p = new Person();
List<Role> roles = p.Roles as List<Role>;
roles.Add(Role.Admin);
Вы можете избежать этого, выполнив итератор:
public IEnumerable<Role> Roles {
get {
foreach (var role in mRoles)
yield return role;
}
}
Ответ 7
Большей проблемой здесь будет публикация Roles
в качестве публичного поля.
следующее выглядит лучше:
public class Person
{
public string Name { get; set; }
private List<Role> _roles = null;
public IEnumerable<Role> Roles
{
get {
if (_roles != null)
return _roles;
else
return Enumerable.Empty<Role>();
}
}
}
И, возможно, вам стоит взглянуть на его возвращение в виде ReadonlyCollection, в зависимости от того, как вы хотите его использовать.
И Enumerable.Empty здесь не better
, немного более эффективен, когда роли обычно остаются пустыми.