【问题标题】:Are there any collections in .NET that prevent null entries?.NET 中是否有任何集合可以防止空条目?
【发布时间】:2011-01-12 06:58:42
【问题描述】:

我特别考虑了一个满足集合合同的集合,但我认为这个问题可以适用于任何类型。 .NET 框架中是否存在防止空条目的集合?我想要的具体行为是这样的:

var set = new HashSet<object>();
bool added = set.Add(null);
Console.WriteLine(added); // prints "False"

这不是内置HashSet&lt;T&gt; 的行为。是否有任何集合具有这种(或类似)行为,或者我最好自己滚动?如果是后者,最好的方法是什么?我应该直接从HashSet&lt;T&gt; 继承还是直接包装它?

编辑:要清楚,这只是无聊的想知道。主要是因为我想不出任何理由想要允许null 进入一组对象。我对此没有任何特殊需要。

【问题讨论】:

  • 不要修复它,它只是隐藏代码中的错误。在调用 Add() 方法之前使用显式 if() 语句。如果你真的必须使用扩展方法。
  • @nobugz +1。这是个好建议。这样做的原因绝对是不是修补损坏的代码。

标签: c# .net generics collections


【解决方案1】:

没有像 HashSet&lt;T&gt; 这样的内置类,除了这个单一的行为。

如果你需要这个,我建议你自己滚动。但是,我不建议继承 HashSet&lt;T&gt;。没有一个方法(如Add,您明确想要更改)是虚拟的,因为它在设计时并没有考虑到子类化。这将导致奇怪的使用行为,因为您将隐藏继承的方法。

只需封装一个HashSet&lt;T&gt;,并公开您需要的成员。您必须添加的唯一真正“代码”是对 Add 方法进行一次空检查 - 否则,只需将所有方法传递给封装的实例。

如果您希望这是一个通用类,则需要添加一个额外的约束以仅与类一起使用,因为您希望进行空检查:

public class ValueSet<T> : ICollection<T>, IEnumerable<T>, ICollection
    where T : class
{
     private HashSet<T> hashSet = new HashSet<T>();
     // ... Implement all members as needed...

【讨论】:

  • 如果您实施ICollection&lt;T&gt;,请确保实施ICollection。后者确实实现了第一个,它使一些代码相当烦人,我想允许同时使用HashTableHashSet&lt;T&gt;...
  • @280Z28:好点。我一直觉得奇怪的是 ICollection 没有实现 ICollection。感谢您添加。
  • 对于想要走这条路的人来说,这似乎是最好的建议。接受。
【解决方案2】:

为 HashSet 写一个扩展方法怎么样?这可能是最容易做的事情。

public static class HashSetExtensions
{
    public static bool AddNonNull<T>(this HashSet<T> set, T item)
        where T : class
    {
        if (item == null)
            return false;

        return set.Add(item);
    }
}

然后你可以这样做:

HashSet<object> test = new HashSet<object>();
test.AddNonNull(null); //Will return false;

【讨论】:

  • 我几乎总是选择避免扩展方法的解决方案,所以我不会使用这个。也就是说,我做了一些更新,所以如果有人这样做,它是可用的。 :)
  • 我也打算这样做,但你不仅比我早了 9 年,你还想添加一个布尔返回值——事实证明,这让我想做的事情变得更容易,而且清洁工:-)
【解决方案3】:

当您使用generics 标记此问题时,我想您正在寻找一个通用集合。这样的集合在 .NET 框架中不存在,仅仅是因为值类型 不能null,所以我想你应该通过在它接受的泛型类型上添加约束限制来滚动你自己的集合。

【讨论】:

  • 你为什么不能用where T : class将它限制为可以为空的对象?
  • 当然,如果你写自己的收藏。
  • @Sean Devlin:这基本上就是我的答案... ;)
【解决方案4】:

System.Collections.Generics.Dictionary&lt;TKey, TValue&gt; 不允许您为TKey 添加null(引发异常)。如果您打算以这种方式使用它,那么您必须在您的场景中忽略TValues,并且功能将类似于Hashset&lt;T&gt;,当然除了花哨的集合操作。

当然有点笨拙,但在您想出自己喜欢的收藏类型之前,这对您来说可能是一个可行的解决方法。

【讨论】:

  • 它也相当昂贵(分配未使用的内存)。
  • 既然这会引发一个必须被捕获的异常,我会说这不是一个好主意..
  • 我自己不会使用这个,只是一个建议:-> ...毕竟,如果异常是明智的,还是不明智的,这取决于场景。
【解决方案5】:

我不知道有任何 .NET 集合会这样做,因为基于传递的类型 null 实际上是一个有效条目,因此添加是成功的。

我很可能会从 System.Collections.ObjectModel.Collection 类开始滚动您自己的类。

【讨论】:

  • Collection 不适合实现 HashSet - 它没有相同的存储语义和性能特征。
【解决方案6】:

即使您设法阻止 ADDING 空条目,您也无法阻止 HAVING 集合内的空条目 - 至少不能通过您的 Set 类。

取任何Set&lt;T&gt;,其中T 是可空类型。添加一些非空条目后,您可以获取其中任何一个并将其设置为空。由于该命令与Set 类无关,因此Set 类无法检测或阻止它。

【讨论】:

  • 好点。但是,您可以通过仅使用带有索引的 Get 和 Set 方法来解决此问题。
【解决方案7】:

两种选择:

编辑:对不起,我错过了他需要一套而不是收藏。至少我列出的首选项目仍然有效。 :)

首选:

在将项目添加到集合的方法中,如果使用无效值调用它们,则抛出 ArgumentNullException

不喜欢:

如果值为 null,则从 Collection&lt;T&gt; 派生并覆盖 InsertItemSetItem 以抛出 ArgumentNullException

【讨论】:

    【解决方案8】:

    在任何情况下,您都可以使用 .NET 4.0 中的代码协定来防止将 null 值添加到集合中。这样,您还将在编译时检查代码是否正确(使用 Code Contracts 提供的静态分析):

    public class MyClass {
      private List<Something> nonNullList;
    
      [ContractInvariantMethod]
      void NonNullEntries() {
        Contract.Invariant(Contract.ForAll(nonNullList, el => el != null));
      }
    
      public void Add(Something el) {
        Contract.Requires(el != null);
      }
    

    }

    标有ContractInvariantMethod 的方法指定应始终保持的条件(列表中没有null 元素)。我相信静态分析无法对此进行推理(因为ForAll),但我可能错了。但是,它当然可以检查您是否使用正确(非空)参数调用 Add 方法。

    【讨论】:

      【解决方案9】:

      正如其他人所回答的那样,没有一个不允许空值的 Set 类。话虽如此,我将分享一个我在几种情况下使用的简单解决方案。就我而言,我正在设计接受 HashSet 参数的方法,因为我需要确保获得唯一的对象。我不希望集合中有一个可能的空值,也不想在每次枚举项目并对它们执行操作时检查空值。我的解决方案是简单地调用 remove null 以防出现空值。

      public void MyMethod(HashSet<SomeType> set)
      {
          // remove null item in case it's there
          set.Remove(null);
      
          foreach (var item in set)
          {
              // safe to assume item is not null
              item.DoSomething();
          }
      }
      

      【讨论】:

        【解决方案10】:

        你必须自己动手。如果您希望 Add 返回一个布尔值(无论该项目是非空的,因此添加还是相反),您必须从头开始。我可能会建议从 List 继承并抛出 ArgumentNullExceptions。我的意思是如果你真的不想添加空值,异常可能是最好的方法,而且肯定会更容易。

        【讨论】:

        • 我不明白你为什么必须从头开始。 HashSet&lt;T&gt;.Add 已经返回 bool。在null 的情况下,似乎可以只返回false,否则推迟到HashSet&lt;T&gt;。编辑:由于该方法不是虚拟的,因此您需要为所有内容明确地推迟到HashSet&lt;T&gt;,这会很痛苦。但总比从头开始好。
        • -1: only 您可以永远List&lt;T&gt;派生的原因是提供一个构造函数,该构造函数采用一种特殊的初始化器或提供新的操作。您绝对不能在 Add 上覆盖其行为。在这里查看我的答案:stackoverflow.com/questions/1192022/…
        猜你喜欢
        • 2021-02-11
        • 2015-09-21
        • 2011-03-05
        • 1970-01-01
        • 1970-01-01
        • 2013-06-07
        • 2019-08-22
        • 2021-06-04
        • 1970-01-01
        相关资源
        最近更新 更多