Лучше ли использовать 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, немного более эффективен, когда роли обычно остаются пустыми.