关于c#:.NET中是否有任何阻止空条目的集合?

Are there any collections in .NET that prevent null entries?

我特别考虑的是一个集合可以履行集合的契约,但我认为这个问题可以应用于任何类型的集合。.NET框架中是否存在阻止空项的集合?我想要的具体行为是:

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

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

编辑:很明显,这只是一种无聊的猜测。主要是因为我想不出任何理由让null进入一组对象。我没有什么特别的需要。


除了这个单一的行为之外,没有一个像HashSet那样的内置类。

如果你需要这个,我建议你自己卷。不过,我不建议将HashSet子类化。没有一种方法(比如你明确想要改变的Add方法)是虚拟的,因为它并不是在设计时考虑到了子类化。这将导致奇怪的使用行为,因为您将隐藏继承的方法。

只需封装一个HashSet,并公开您需要的成员。您需要添加的唯一真正的"代码"是对a d d方法进行一次空检查——否则,只需将所有方法传递给封装的实例。

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

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


写一个哈希集的扩展方法怎么样?这可能是最简单的事情。

1
2
3
4
5
6
7
8
9
10
11
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);
    }
}

然后您可以这样做:

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


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


即使您设法阻止添加空条目,也无法阻止在集合中包含空条目——至少不能通过您的Set类来阻止。

Set为例,其中T是可以为空的类型。在添加了一些非空条目之后,可以获取其中任何一个条目并将其设置为空。由于该命令与Set类无关,因此Set类无法检测到它,也无法阻止它。


System.Collections.Generics.Dictionary不允许您为TKey添加null(抛出异常)。如果您打算这样使用,那么在您的场景中必须忽略TValue,并且功能将类似于HashSet,当然除了花哨的设置操作。

当然有点笨拙,但在你想出自己的收藏类型之前,也许这对你来说是一个很好的解决办法。


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

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


正如其他人所回答的,没有不允许空值的集合类。说了这些之后,我将分享一个我在一些情况下使用的简单解决方案。在我的例子中,我正在设计接受哈希集参数的方法,因为我需要确保获得唯一的对象。我不希望在集合中出现可能的空值,也不希望每次枚举项并对其执行操作时都检查空值。我的解决方案是简单地调用remove null,以防出现空值。

1
2
3
4
5
6
7
8
9
10
11
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();
    }
}

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

1
2
3
4
5
6
7
8
9
10
11
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方法。


两种选择:

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

优选:

在向集合中添加项的方法中,如果使用无效值调用了ArgumentNullException,则抛出它。

不优选:

Collection派生,如果值为空,则重写InsertItemSetItem以抛出ArgumentNullException


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