是否更好地使用Enumerable.Empty <T>()而不是新的List <T>()来初始化IEnumerable <T>?

Sté*_*ane 133 c# linq constructor

假设你有一个人类:

public class Person
{
   public string Name { get; set;}
   public IEnumerable<Role> Roles {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

我显然应该在构造函数中实例化角色.现在,我曾经使用像这样的List:

public Person()
{
   Roles = new List<Role>();
}
Run Code Online (Sandbox Code Playgroud)

但我在System.Linq命名空间中发现了这种静态方法

IEnumerable<T> Enumerable.Empty<T>();
Run Code Online (Sandbox Code Playgroud)

来自MSDN:

Empty(TResult)()方法缓存一个空的序列TResult.当它返回的对象被枚举时,它不会产生任何元素.

在某些情况下,此方法对于将空序列传递给用户定义的方法非常有用 IEnumerable(T).它还可以用于为诸如的方法生成中性元素Union.有关此用法的示例,请参阅示例部分

那么编写这样的构造函数会更好吗?你用它吗?为什么?如果没有,为什么不呢?

public Person()
{
   Roles = Enumerable.Empty<Role>();
}
Run Code Online (Sandbox Code Playgroud)

Vad*_*kan 154

我认为大多数帖子都错过了重点.即使您使用空数组或空列表,这些也是对象,它们存储在内存中.比垃圾收集者必须照顾他们.如果您正在处理高吞吐量应用程序,它可能会产生明显的影响.

Enumerable.Empty 不会为每个调用创建一个对象,从而减少了GC的负担.

如果代码处于低吞吐量位置,那么它可归结为美学考虑因素.

  • 而且它也更好地反映了意图. (14认同)
  • @MohammedNoureldin它返回单身人士 (9认同)
  • @Lennart我不这么认为。您提供的代码使用了“Array.Empty&lt;TResult&gt;()”,它是一个函数,而不是构造函数。这个函数返回`EmptyArray&lt;T&gt;.Value`,它又使用`public static readonly T[] Value = new T[0];`所以使用的单例已经改变,但它仍然是单例。 (6认同)
  • 如果没有创建对象,`Empty()`如何工作? (4认同)
  • `Enumerable.Empty&lt;&gt;` 不仅返回一个单例枚举,该枚举还返回一个单例枚举器。 (2认同)
  • 确实,你是对的。应该事先检查“Empty&lt;TResult&gt;()”函数。 (2认同)

Tom*_*ier 86

我认为Enumerable.Empty<T>更好,因为它更明确:你的代码清楚地表明了你的意图.它也可能更高效,但这只是次要优势.

  • 是.可枚举<>.Empty确实避免分配一个新对象,这是一个小小的奖励. (25认同)
  • 旁注@NeilWhitaker:它是Enumerable.Empty <T>不可枚举<T> .Empty(那个让我疯狂的一次......) (11认同)
  • 是的,清楚地表明你的意思是空的,而后来可能添加的东西对你的代码有好处. (6认同)

Dre*_*kes 21

在性能方面,让我们看看如何Enumerable.Empty<T>实现.

它返回EmptyEnumerable<T>.Instance,定义为:

internal class EmptyEnumerable<T>
{
    public static readonly T[] Instance = new T[0];
}
Run Code Online (Sandbox Code Playgroud)

泛型类型的静态字段按泛型类型参数分配.这意味着运行时只能为用户代码需要的类型延迟创建这些空数组,并根据需要重复使用实例,而不会对垃圾收集器施加任何压力.

以机智:

Debug.Assert(ReferenceEquals(Enumerable.Empty<int>(), Enumerable.Empty<int>()));
Run Code Online (Sandbox Code Playgroud)

  • 所有这一切,而我认为实现就像`public static IEnumerable&lt;T&gt; Empty&lt;T&gt;() { yield break; }` MS 最了解它,可能缓存的 `new T[0]` 更有效。 (3认同)

Nei*_*ell 12

假设你实际上想要以Roles某种方式填充属性,然后通过使它的setter私有并将其初始化为构造函数中的新列表来封装它:

public class Person
{
    public string Name { get; set; }
    public IList<Role> Roles { get; private set; }

    public Person()
    {
        Roles = new List<Role>();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你真的想拥有公共setter,请保留Rolesnull并避免对象分配.


Lee*_*Lee 7

您的方法的问题是您不能将任何项添加到集合 - 我将有一个像列表的私有结构,然后将项目公开为Enumerable:

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(); }
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你打算让其他一些类创建角色列表(我不推荐),那么我就不会在Person中初始化所有可枚举的类.


Han*_*ant 6

将私有List暴露为IEnumerable的典型问题是,您的类的客户端可以通过强制转换来解决它.这段代码可行:

  var p = new Person();
  List<Role> roles = p.Roles as List<Role>;
  roles.Add(Role.Admin);
Run Code Online (Sandbox Code Playgroud)

您可以通过实现迭代器来避免这种情况:

public IEnumerable<Role> Roles {
  get {
    foreach (var role in mRoles)
      yield return role;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果开发人员将您的IEnumerable <T>转换为List <T>,那么这就是他的问题而不是你的问题.如果您将来更改内部实现(因此它不再是List <T>),那么开发人员的代码中断并不是您的错.你无法阻止人们编写蹩脚的代码并滥用你的API,所以我认为不值得花费很多精力. (4认同)
  • 这不是关于破坏代码,而是关于利用代码. (3认同)
  • 如果这是一个安全问题,我同意@Hans,在所有其他情况下我同意@Tommy。顽皮的开发者总是会犯这样一个致命的错误。 (2认同)

Hen*_*man 5

这里更大的问题是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>();
        }
   }
}
Run Code Online (Sandbox Code Playgroud)

也许您应该看一下将其作为ReadonlyCollection返回的情况,具体取决于您要如何使用它。

而且Enumerable.Empty不在better这里,当Role通常为空时,效率要高一点。

  • 我不同意广泛的说法,即公共领域是不好的。但是,这不是进行该辩论的合适场所。只需说说您已经接受了1行文字并将其上移至10行,而没有为OP的问题提供有用的答案。 (3认同)