关于c#:使用Enumerable.Empty< T>()而不是新的List< T>()来初始化IEnumerable< T>?

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

假设你有一个班上的人:

1
2
3
4
5
public class Person
{
   public string Name { get; set;}
   public IEnumerable<Role> Roles {get; set;}
}

显然,我应该实例化构造函数中的角色。现在,我用这样的列表来做:

1
2
3
4
public Person()
{
   Roles = new List<Role>();
}

但是我在System.Linq名称空间中发现了这个静态方法

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

来自MSDN:

The Empty(TResult)() method caches an
empty sequence of type 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). It can also be used to
generate a neutral element for methods
such as Union. See the Example section
for an example of this use of

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

1
2
3
4
public Person()
{
   Roles = Enumerable.Empty<Role>();
}


我认为大多数帖子都没有抓住要点。即使使用空数组或空列表,它们也是对象,并存储在内存中。比垃圾收集者还要照顾他们。如果您处理的是高吞吐量应用程序,那么它可能会受到显著的影响。

Enumerable.Empty不在每次调用中创建对象,因此减少了GC的负载。

如果代码处于低吞吐量位置,那么它归根结底就是美学上的考虑。


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


在性能方面,我们来看看Enumerable.Empty是如何实现的。

返回EmptyEnumerable.Instance,定义为:

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

泛型类型上的静态字段是按泛型类型参数分配的。这意味着运行时可以仅为用户代码需要的类型而延迟地创建这些空数组,并且可以根据需要多次重用实例,而不会对垃圾收集器施加任何压力。

才智:

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


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

1
2
3
4
5
6
7
8
9
10
public class Person
{
    public string Name { get; set; }
    public IList<Role> Roles { get; private set; }

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

如果您真的想拥有公共setter,请将Roles的值保留为null,并避免对象分配。


您的方法的问题是,您不能向集合中添加任何项-我将具有类似list的私有结构,然后将这些项作为可枚举项公开:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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(); }
    }
}

如果您希望其他类创建角色列表(我不推荐),那么我不会亲自初始化可枚举的。


将私有列表公开为IEnumerable的典型问题是类的客户机可以通过强制转换来处理它。此代码可以工作:

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

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

1
2
3
4
5
6
public IEnumerable<Role> Roles {
  get {
    foreach (var role in mRoles)
      yield return role;
  }
}


更大的问题是将Roles作为公共领域公开。

以下内容看起来更好:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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返回它,这取决于你想如何使用它。

而且可以枚举。这里的better不是空的,只是在角色通常保持为空的情况下效率会提高一点。