.NET中是否有任何阻止空条目的集合?

时间:2010-02-02 18:15:38

标签: c# .net generics collections

我特别想到的是一个符合一套合同的集合,但我认为这个问题适用于任何类型。 .NET框架中是否存在阻止空条目的集合?我想要的具体行为是:

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

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

编辑:要清楚,这只是空闲的疑惑。主要是因为我无法想到任何我希望允许null进入一组对象的原因。我对此没有任何特别需要。

10 个答案:

答案 0 :(得分:12)

除了这一行为外,没有像HashSet<T>这样的内置类。

如果你需要这个,我建议你自己动手。但是,我不建议继承HashSet<T>。没有一个方法(比如你明确想要改变的Add)都是虚拟的,因为它并没有真正考虑到子类化。这将导致奇怪的使用行为,因为您将隐藏继承的方法。

只需封装HashSet<T>,然后展示您需要的成员。您必须添加的唯一真正的“代码”是对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...

答案 1 :(得分:7)

如何为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;

答案 2 :(得分:3)

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

答案 3 :(得分:1)

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

我很可能从System.Collections.ObjectModel.Collection类开始,用于滚动自己的。

答案 4 :(得分:1)

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

肯定有点笨拙,但在此之前,对你来说可能是一个负责任的解决方法,然后才能想出你自己喜欢的收藏类型。

答案 5 :(得分:1)

即使你设法阻止ADDING空条目,你也无法阻止在集合中使用HAVING空条目 - 至少不能通过你的let fnAddNodeFileAndArray (pstrnode:string) lintRowResult:int = //Declare variables let mutable lstrText = "" let mutable larrColisionsAux = Array2D.zeroCreate 0 0 let mutable lintIndexOfSpace = 0 let mutable lstrfirstNumber = "" let mutable lstrsecondNumber = "" let mutable lintNumberColisionNetwork = 0 let mutable lblnCollisionExist = false let mutable lintRowResultAux=0 //Identify the position where the separator lintIndexOfSpace<-pstrnode.IndexOf(",") //Get nodes of the collision lstrfirstNumber<-(pstrnode.Substring(0,lintIndexOfSpace)) lstrsecondNumber<-(pstrnode.Substring(lintIndexOfSpace + 1)) for lintRow = 0 to garrrows - 1 do let mutable lintNumberAux1 = int garrColisions.[lintRow,0] let mutable lintNumberAux2 = int garrColisions.[lintRow,1] if (string lstrfirstNumber = string lintNumberAux1 && string lstrsecondNumber = string lintNumberAux2) || (string lstrfirstNumber = string lintNumberAux1 && string lstrsecondNumber = string lintNumberAux2) then lblnCollisionExist <- true if lblnCollisionExist=true then printfn "" printfn "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" printfn "Node already exist." printfn "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" lintRowResultAux else //Copy the values of the array to auxiliar array larrColisionsAux<-garrColisions //Update de number of collisions garrrows <- garrrows + 1 //Re-Create array garrColisions <- Array2D.zeroCreate garrrows 3 for lintRow = 0 to garrrows - 2 do garrColisions.[lintRow,0] <- larrColisionsAux.[lintRow,0] garrColisions.[lintRow,1] <- larrColisionsAux.[lintRow,1] garrColisions.[lintRow,2] <- larrColisionsAux.[lintRow,2] //Save the new Number in auxiliar Array garrColisions.[(garrrows-1),0] <- string lstrfirstNumber garrColisions.[(garrrows-1),1] <- string lstrsecondNumber //Save the number in the file for lintCount = 0 to garrrows-1 do lstrText <- lstrText + garrColisions.[lintCount,0] + " " + garrColisions.[lintCount,1] + "\n" File.WriteAllText(gstrpath,lstrText) for lintRow = 0 to (garrrows - 2) do let mutable lintNumberAux1 = int garrColisions.[lintRow,0] let mutable lintNumberAux2 = int garrColisions.[lintRow,1] let mutable lintNumberCollisionNetworkAux = int garrColisions.[lintRow,2] let mutable lblnLessOneNumberOfTheCollisionNetwork = false if (lstrfirstNumber <> string lintNumberAux1 ) && (lstrsecondNumber <> string lintNumberAux2) && (lstrfirstNumber <> string lintNumberAux2) && (lstrsecondNumber <> string lintNumberAux1) then if lintNumberColisionNetwork = 0 then gintNumberColisionNetwork <- gintNumberColisionNetwork + 1 garrColisions.[(garrrows-1),2] <- string (gintNumberColisionNetwork) elif ((lstrfirstNumber = string lintNumberAux1) && (lstrsecondNumber <> string lintNumberAux2)) || ((lstrfirstNumber <> string lintNumberAux1) && (lstrsecondNumber = string lintNumberAux2)) || ((lstrfirstNumber <> string lintNumberAux2) && (lstrsecondNumber = string lintNumberAux1)) || ((lstrfirstNumber = string lintNumberAux2) && (lstrsecondNumber <> string lintNumberAux1)) then if lintNumberColisionNetwork < lintNumberCollisionNetworkAux then garrColisions.[(garrrows-1),2] <- garrColisions.[lintRow,2] if lintNumberColisionNetwork <> 0 && lblnLessOneNumberOfTheCollisionNetwork = false then gintNumberColisionNetwork <- gintNumberColisionNetwork - 1 lblnLessOneNumberOfTheCollisionNetwork <- true lintNumberColisionNetwork <- int garrColisions.[lintRow,2] elif lintNumberColisionNetwork > lintNumberCollisionNetworkAux then garrColisions.[lintRow,2] <- garrColisions.[(garrrows-1),2] if lintNumberColisionNetwork <> 0 && lblnLessOneNumberOfTheCollisionNetwork = false then gintNumberColisionNetwork <- gintNumberColisionNetwork - 1 lblnLessOneNumberOfTheCollisionNetwork <- true lintNumberColisionNetwork <- int garrColisions.[lintRow,2] garrrows let fnAddnode () = let mutable lintrow = 0 let mutable lstrNode = "" lstrNode <- fnGetNewNode() lintrow <- fnAddNodeFileAndArray(lstrNode) fnPrintCollisionOneNode (lintrow) 类。

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

答案 6 :(得分:0)

两个选项:

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

优选:

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

不是首选:

Collection<T>派生并覆盖InsertItemSetItem,如果值为null,则抛出ArgumentNullException

答案 7 :(得分:0)

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

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方法。

答案 8 :(得分:0)

正如其他人已经回答的那样,没有一个Set类不允许空值。话虽如此,我将分享一些我在一些情况下使用的简单解决方案。就我而言,我正在设计接受HashSet参数的方法,因为我需要确保我得到唯一的对象。我不想在集合中存在可能的null,并且每次我枚举项目并对它们执行操作时我都不想检查null。我的解决方案是在没有空的情况下简单地调用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();
    }
}

答案 9 :(得分:-1)

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