与新 List < T > ()相比,使用 Enumery.Empty < T > ()来初始化 IEnumerable < T > 更好吗?

假设你有一个类 Person:

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

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

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

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

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

来自 MSDN:

Empty(TResult)()方法缓存一个 类型为 TResult的空序列 它返回的对象是枚举的, 它不会产生任何元素。

在某些情况下,这种方法是有用的 将空序列传递给 用户定义的方法,该方法接受 它也可以用来 为方法生成一个中性元素 例如 Union。请参阅示例部分 例子

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

public Person()
{
Roles = Enumerable.Empty<Role>();
}
54907 次浏览

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

您的方法的问题在于您不能向集合中添加任何项目——我将使用一个类似 list 的私有结构,然后将这些项目作为枚举暴露出来:

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 中初始化可枚举。

假设您确实想以某种方式填充 Roles属性,然后将其封装为 setter 私有属性,并在构造函数中将其初始化为一个新列表:

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 公开为 IEnumable 的典型问题是,类的客户端可以通过强制转换来扰乱它。这个代码可行:

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

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

Enumerable.Empty不会为每次调用创建一个对象,因此会减少 GC 的负载。

如果代码处于低吞吐量位置,那么这就归结为审美方面的考虑。

在性能方面,让我们看看 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>()));