简体   繁体   English

使用 Enumerable.Empty 是否更好<T> () 而不是 new List<T> () 初始化一个 IEnumerable<T> ?

[英]Is it better to use Enumerable.Empty<T>() as opposed to new List<T>() to initialize an IEnumerable<T>?

Suppose you have a class Person :假设你有一个类 Person :

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

I should obviously instantiate the Roles in the constructor.我显然应该在构造函数中实例化角色。 Now, I used to do it with a List like this :现在,我曾经用这样的列表来做:

public Person()
{
   Roles = new List<Role>();
}

But I discovered this static method in the System.Linq namespace但是我在System.Linq命名空间中发现了这个静态方法

IEnumerable<T> Enumerable.Empty<T>();

From MSDN :MSDN

The Empty(TResult)() method caches an empty sequence of type TResult . Empty(TResult)()方法缓存TResult类型的空序列。 When the object it returns is enumerated, it yields no elements.当枚举它返回的对象时,它不会产生任何元素。

In some cases, this method is useful for passing an empty sequence to a user-defined method that takes an IEnumerable(T) .在某些情况下,此方法可用于将空序列传递给采用IEnumerable(T)的用户定义方法。 It can also be used to generate a neutral element for methods such as Union .它还可用于为诸如Union方法生成中性元素。 See the Example section for an example of this use of有关此用法的示例,请参阅示例部分

So is it better to write the constructor like that?那么这样编写构造函数会更好吗? Do you use it?你使用它吗? Why?为什么? or if not, Why not?如果没有,为什么不呢?

public Person()
{
   Roles = Enumerable.Empty<Role>();
}

I think most postings missed the main point.我认为大多数帖子都没有抓住要点。 Even if you use an empty array or empty list, those are objects and they are stored in memory.即使您使用空数组或空列表,它们也是对象并且它们存储在内存中。 The Garbage Collector has to take care of them.垃圾收集器必须照顾它们。 If you are dealing with a high throughput application, it could be a noticeable impact.如果您正在处理高吞吐量应用程序,这可能会产生明显的影响。

Enumerable.Empty does not create an object per call thus putting less load on the GC. Enumerable.Empty不会在每次调用时创建一个对象,从而减少了 GC 的负载。

If the code is in low-throughput location, then it boils down to aesthetic considerations though.如果代码位于低吞吐量位置,那么它归结为美学考虑。

I think Enumerable.Empty<T> is better because it is more explicit: your code clearly indicates your intentions.我认为Enumerable.Empty<T>更好,因为它更明确:您的代码清楚地表明了您的意图。 It might also be a bit more efficient, but that's only a secondary advantage.它也可能更有效率,但这只是次要优势。

On the performance front, let's see how Enumerable.Empty<T> is implemented.在性能方面,让我们看看Enumerable.Empty<T>是如何实现的。

It returns EmptyEnumerable<T>.Instance , which is defined as:它返回EmptyEnumerable<T>.Instance ,其定义为:

internal class EmptyEnumerable<T>
{
    public static readonly T[] Instance = new T[0];
}

Static fields on generic types are allocated per generic type parameter.泛型类型的静态字段按泛型类型参数分配。 This means that the runtime can lazily create these empty arrays only for the types user code needs, and reuse the instances as many times as needed without adding any pressure on the garbage collector.这意味着运行时可以只为用户代码需要的类型懒惰地创建这些空数组,并根据需要多次重用实例,而不会给垃圾收集器增加任何压力。

To wit:以机智:

Debug.Assert(ReferenceEquals(Enumerable.Empty<int>(), Enumerable.Empty<int>()));

Assuming you actually want to populate the Roles property somehow, then encapsulate that by making it's setter private and initialising it to a new list in the constructor:假设您确实想以某种方式填充Roles属性,然后通过将其设置为私有并将其初始化为构造函数中的新列表来封装它:

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

    public Person()
    {
        Roles = new List<Role>();
    }
}

If you really really want to have the public setter, leave Roles with a value of null and avoid the object allocation.如果您真的想要公共设置器,请将Roles的值保留为null并避免对象分配。

The problem with your approach is that you can't add any items to the collection - I would have a private structure like list and then expose the items as an Enumerable:你的方法的问题是你不能向集合中添加任何项目 - 我会有一个像 list 这样的私有结构,然后将这些项目公开为一个 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(); }
    }
}

If you intend some other class to create the list of roles (which I wouldn't recommend) then I wouldn't initialise the enumerable at all in Person.如果您打算使用其他类来创建角色列表(我不建议这样做),那么我根本不会亲自初始化可枚举项。

The typical problem with exposing the private List as an IEnumerable is that the client of your class can mess with it by casting.将私有列表公开为 IEnumerable 的典型问题是您的类的客户端可能会通过强制转换来弄乱它。 This code would work:这段代码可以工作:

  var p = new Person();
  List<Role> roles = p.Roles as List<Role>;
  roles.Add(Role.Admin);

You can avoid this by implementing an iterator:您可以通过实现迭代器来避免这种情况:

public IEnumerable<Role> Roles {
  get {
    foreach (var role in mRoles)
      yield return role;
  }
}

The larger problem here would be exposing Roles as a public field . 这里更大的问题是将Roles暴露为公共领域

the following looks better: 以下看起来更好:

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>();
        }
   }
}

And maybe you should take a look at returning it as a ReadonlyCollection , depending on how you want to use it. 也许您应该看一下将其作为ReadonlyCollection返回,具体取决于您希望如何使用它。

And Enumerable.Empty isn't better here, just a little more efficient when Roles usually stays empty. 并且Enumerable.Empty在这里并不是better ,当角色通常保持空置时效率更高一些。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM