【问题标题】:C# / .NET equivalent for Java Collections.<T>emptyList()?Java Collections.<T>emptyList() 的 C#/.NET 等效项?
【发布时间】:2023-03-23 20:55:02
【问题描述】:

在 C# 中获取类型化的只读空列表的标准方法是什么,或者有什么方法?

ETA:对于那些问“为什么?”的人:我有一个返回 IList 的虚拟方法(或者更确切地说,是回复后的 IEnumerable),默认实现是空的。无论列表返回什么都应该是只读的,因为写入它会是一个错误,如果有人尝试这样做,我想立即停止并着火,而不是等待错误稍后以某种微妙的方式出现。

【问题讨论】:

  • 你将如何处理只读的空列表?只是好奇。
  • 我猜一个空的 IEnumerable&lt;T&gt; 可能是正确的答案 - 真的需要 IList 吗?还是只是一个空的只读集合?
  • 只是好奇,你为什么需要一个空的只读列表?
  • IEnumerable&lt;T&gt; 在这种情况下可以正常工作,谢谢。
  • 如果IList 只是一个序列,我们就不需要它,我们只需要使用IEnumerable。如果您想要任何ICollection 方法,则至少需要ICollection,如果您想要索引访问,则至少需要IListICollection&lt;T&gt; 中的修改操作是可选的,这就是为什么有 IsReadOnly() 以及为什么 Add() 等被记录为抛出 NotSupportedException

标签: c# collections generic-collections


【解决方案1】:

就个人而言,我认为这比其他任何答案都好:

static readonly IList<T> EmptyList = new T[0];
  • 数组实现IList&lt;T&gt;
  • 您不能添加到数组中。
  • 您不能分配给空数组中的元素(因为那里 没有)。
  • 在我看来,这比new List&lt;T&gt;().AsReadOnly() 简单得多。
  • 您仍然可以返回 IList&lt;T&gt;(如果需要)。

顺便说一句,如果我没记错的话,这就是Enumerable.Empty&lt;T&gt;() 在后台实际使用的内容。所以理论上你甚至可以做(IList&lt;T&gt;)Enumerable.Empty&lt;T&gt;()(虽然我认为没有充分的理由这样做)。

【讨论】:

  • 你的回答真的只对我有意义
  • 从 4.6 开始,您甚至不需要再提供自己的对象,只需返回 Array.Empty()。
【解决方案2】:

你可以创建一个列表:

List<MyType> list = new List<MyType>();

如果您想要一个空的IEnumerable&lt;T&gt;,请使用Enumerable.Empty&lt;T&gt;()

IEnumerable<MyType> collection = Enumerable.Empty<MyType>();

如果你真的想要一个只读列表,你可以这样做:

IList<MyType> readonlyList = (new List<MyType>()).AsReadOnly();

这会返回一个ReadOnlyCollection&lt;T&gt;,它实现了IList&lt;T&gt;

【讨论】:

  • 我也这么认为,但后来看到了只读要求。然后我想一个空的只读 List 有什么意义。我错过了什么?
  • @JayR 一个空的只读集合是 API 返回值的理想选择,以便在不诉诸 null 的情况下传达“无结果”的想法。而且,理想情况下,您希望引用同一个只读的空集合,原因与您希望使用 string.Empty 的原因相同:内存保护。
【解决方案3】:

从 .net 4.6 开始,您还可以使用:

IList<T> emptyList = Array.Empty<T>();

这只会为您指定为 T 的每个不同类型创建一次新实例。

【讨论】:

  • 就像 String.Empty 一样,它也总是引用同一个实例,而不是创建一个新的空字符串。
  • @David De Sloovere 空字符串文字 "" 也引用了相同的实例,这要归功于字符串实习(只是告诉那些觉得有必要用 String.Empty 替换 "" 的读者)。
【解决方案4】:
IList<T> list = new List<T>().AsReadOnly();

或者,如果你想要IEnumerable&lt;&gt;

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

【讨论】:

  • 从4.6开始,有更好的解决方案:IList list = Array.Empty();
【解决方案5】:

如果你想要一个内容不能修改的列表,你可以这样做:

ReadOnlyCollection<Foo> foos = new List<Foo>().AsReadOnly();

【讨论】:

    【解决方案6】:

    从您的列表中构造一个System.Collections.ObjectModel.ReadOnlyCollection 的实例。

    List<int> items = new List<int>();
    ReadOnlyCollection<int> readOnlyItems = new ReadOnlyCollection<int>(items);
    

    【讨论】:

      【解决方案7】:

      要扩展Dan Tao's answer,可以按照与Enumerable.Empty&lt;T&gt;() 相同的方式使用以下实现,只需指定List.Empty&lt;T&gt;()

      public static class List
      {
          public static IList<T> Empty<T>()
          {
              // Note that the static type is only instantiated when
              // it is needed, and only then is the T[0] object created, once.
              return EmptyArray<T>.Instance;
          }
      
          private sealed class EmptyArray<T>
          {
              public static readonly T[] Instance = new T[0];
          }
      }
      

      编辑:我更改了上面的代码,以反映与 Dan Tao 关于 Instance 字段的惰性初始化与急切初始化的讨论结果。

      【讨论】:

      • 零长度数组的延迟初始化?我个人认为这是一个错误,因为它增加了复杂性,会使性能稍微变差(每次访问都会调用额外的方法),有竞争条件(两个线程同时调用 List.Empty&lt;T&gt;() 可能会得到不同的对象)并且几乎买了你什么都没有(您认为单个零长度数组的初始化成本与 JIT 编译新类的初始化成本相比如何?)。使用急切初始化的 static readonly 公共字段,或者 - 如果您的良心不允许 - 使用私有字段加上 Empty 方法。只是我的两分钱!
      • 两个线程获取不同的对象不是问题。由于呼叫是very likely inlined,因此性能不受影响。我看不出一行实际逻辑如何严重增加复杂性。最后但同样重要的是:我刚刚检查了Enumerable.Empty&lt;T&gt;() 的实现,它与我写的非常相似。如果一个人需要所有类型的空列表,请使用这种方法,因为它可以节省分配和垃圾。如果您很少需要一个空列表,请使用您的static readonly
      • 您的防御可能是正确的,因为我的评论肯定是分裂的头发。但是,例如,如果您有一些依赖于检查 if (list == List.Empty&lt;T&gt;()) 的关键代码,则不同的对象可能会成为问题。此外,这种方法如何节省分配和垃圾?
      • 好吧,与你的相比,我的方法不会节省垃圾。但它会节省分配:任何时候你写static readonly IList&lt;T&gt; EmptyList = new T[0]你分配另一个T[0]对象。如果您使用 n 不同的类型 T,您将分配 至少 n T[0] 对象,而我的方法将准确分配 n T[0] 对象.顺便说一句,程序员不应该依赖list == List.Empty&lt;T&gt;()(或e == Enumerable.Empty&lt;T&gt;(),因为它有同样的问题)。 Empty&lt;T&gt;() 毕竟是一个方法,而且方法通常不保证总是返回相同的值。
      • 好的,对。您已经证明,直接访问静态字段(在我的 PC 上)比通过另一个类中的方法访问它快两倍。虽然它并不是真正关于延迟初始化,但您关于性能的初始论点是正确的。显然,过早的优化永远不应该成为争论,但我开始热身于你的热切方法。我对其进行了一些测试,并得出结论,无论哪种方式,类型的创建都不会早于第一次需要它们的时候。您认为 .NET 框架开发人员为什么也选择了惰性方法?
      【解决方案8】:

      怎么样:

      readonly List<T> mylist = new List<T>();
      

      不知道为什么你想要它只读;不过,在我能想到的大多数情况下,这没有多大意义。

      【讨论】:

        猜你喜欢
        • 2015-02-25
        • 2016-08-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多