C# 使用Enumerable.Empty是否更好<;T>;()与新列表相反<;T>;()初始化IEnumerable<;T>;?

C# 使用Enumerable.Empty是否更好<;T>;()与新列表相反<;T>;()初始化IEnumerable<;T>;?,c#,linq,constructor,C#,Linq,Constructor,假设你有一个班上的人: public class Person { public string Name { get; set;} public IEnumerable<Role> Roles {get; set;} } 发件人: Empty(TResult)(方法缓存 类型为TResult的空序列。什么时候 它返回的对象被枚举, 它不产生任何元素 在某些情况下,此方法很有用 用于将空序列传递给 采用 IEnumerable(T)。它也可以用来 为方法生成中性元素 例如

假设你有一个班上的人:

public class Person
{
   public string Name { get; set;}
   public IEnumerable<Role> Roles {get; set;}
}
发件人:

Empty(TResult)(
方法缓存 类型为
TResult
的空序列。什么时候 它返回的对象被枚举, 它不产生任何元素

在某些情况下,此方法很有用 用于将空序列传递给 采用
IEnumerable(T)
。它也可以用来 为方法生成中性元素 例如
联合
。请参见示例部分 例如,使用

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

public Person()
{
   Roles = Enumerable.Empty<Role>();
}
公众人物()
{
Roles=Enumerable.Empty();
}

我认为
是可枚举的。Empty
更好,因为它更明确:您的代码清楚地表明了您的意图。它可能会更高效一些,但这只是第二个优势。

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

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(); }
    }
}
公共类人物
{
私人IList_角色;
公众人士()
{
这是。_roles=new List();
}
公共字符串名称{get;set;}
public void AddRole(角色角色)
{
//实施
}
公共可数角色
{
获取{返回此。_roles.AsEnumerable();}
}
}

如果您想让其他类创建角色列表(我不建议这样做),那么我不会亲自初始化可枚举项。

假设您确实希望以某种方式填充
角色属性,然后通过将其setter设置为私有并将其初始化到构造函数中的新列表来封装该属性:

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

    public Person()
    {
        Roles = new List<Role>();
    }
}
公共类人物
{
公共字符串名称{get;set;}
公共IList角色{get;private set;}
公众人士()
{
角色=新列表();
}
}

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

将私有列表公开为IEnumerable的典型问题是,您的类的客户端可以通过强制转换来处理它。该代码将在以下情况下起作用:

  var p = new Person();
  List<Role> roles = p.Roles as List<Role>;
  roles.Add(Role.Admin);
var p=newperson();
列表角色=p.作为列表的角色;
roles.Add(Role.Admin);
可以通过实现迭代器来避免这种情况:

public IEnumerable<Role> Roles {
  get {
    foreach (var role in mRoles)
      yield return role;
  }
}
公共IEnumerable角色{
得到{
foreach(mRoles中的var角色)
收益回报作用;
}
}

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

Enumerable.Empty
不会为每次调用创建对象,从而减少GC的负载


如果代码处于低吞吐量位置,那么可以归结为美学考虑。

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

它返回
EmptyEnumerable.Instance
,定义为:

internal class EmptyEnumerable<T>
{
    public static readonly T[] Instance = new T[0];
}
内部类EmptyEnumerable
{
公共静态只读T[]实例=新T[0];
}
泛型类型上的静态字段是按泛型类型参数分配的。这意味着运行时只能为用户代码需要的类型惰性地创建这些空数组,并根据需要多次重用实例,而不会给垃圾收集器增加任何压力

也就是说:

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

这是一个很好的观点。事实上,我会在我的实现中很快发现这一点:)我会将_角色设置为只读,并且我认为没有必要调用AsEnumerable()。否则,+1@Kent-我同意readonly。至于AsEnumerable,实际上并不需要它,但它可以防止角色被转换到IList并进行修改。是的,明确表示这是空的,而不是以后可能添加到的内容,这对您的代码有好处。是的。Enumerable.Empty确实避免分配新对象,这是一个小小的奖励。sidenote@NeilWhitaker:它是可枚举的。Empty不是可枚举的。Empty(那个曾经让我发疯过…)同意。添加;如果你需要初始化一个
IEnumerable
…为什么要用比你需要更多的火力来初始化它呢<代码>列表
是一个更复杂的对象,它实现了
IEnumerable
,以及许多其他功能。我说用最基本的方法初始化属性,在这种情况下也使用
Enumerable.Empty()
。这是一个数据类。在使用EntityFramework4.0实现存储库模式时,我打算将该类用作模型类(玩转…)。所以我认为在这里有一个公共setter很好,不是吗?serbech,当其他代码可以为列表安装任何类型的IEnumerable派生类时,如何添加角色(内部人员)?我明白你的意思,我应该在内部有一个IList,或者甚至在这里有一个列表?这将消除我原来的问题…我在单元测试中使用Enumerable.Empty来表示不满意的路径测试,以帮助传达意图。正如许多人已经指出的那样,当您的单元测试数量达到数百个或更多时,GC上没有分配的好处是有帮助的。现在还可以使用
Array.Empty()
,它显式地是一个数组,因此是一个
IList
。在Core中,它实际上是相同的数组,只是暴露的类型不同。如果开发人员将IEnumerable强制转换为列表,那么这是他的问题,而不是你的问题。如果您将来更改内部实现(因此不再列出),那么开发人员的代码中断不是您的错。你不能阻止人们编写蹩脚的代码和滥用你的API,所以我不这么认为
internal class EmptyEnumerable<T>
{
    public static readonly T[] Instance = new T[0];
}
Debug.Assert(ReferenceEquals(Enumerable.Empty<int>(), Enumerable.Empty<int>()));