C#代码合同 - 如何确保项目集合包含具有唯一属性的项目?

时间:2011-12-13 15:36:00

标签: c# linq collections code-contracts

基本上,我有以下内容:

public class MyClass
{
    public MyClass(ICollection<MyObject> coll)
    {
        Contract.Requires(coll != null);
        Contract.Requires(Contract.ForAll(coll, obj => obj != null));
        Contract.Requires(Contract.ForAll(coll, obj => (????)); //What goes here?
    }
}

public class MyObject
{
    public object PropA { get; set; }
    public object PropB { get; set; }
}

要求是:

  • 集合中的所有PropA项目​​都是唯一的(无重复项目)
  • 集合中的所有PropB项目都是唯一的(没有重复项目)

似乎无法弄清楚我的Contract.ForAll(...)语句要做什么。


奖励:如果我可以在不破坏代码合同的情况下合并Contract.ForAll(...)语句?

2 个答案:

答案 0 :(得分:2)

我可能完全不在这里,从未使用合同,但假设Contract.Requires可以传递任意bool,你不能这样做:

Contract.Requires(coll.GroupBy(o => o.PropA).Count() == coll.Count);

,同样适用于PropB

答案 1 :(得分:1)

我相信以下应该可以解决问题:

Contract.Requires(
    Contract.ForAll(
        coll, 
        obj => (coll.Where(x=>x.PropA = obj.PropA).Count==1)
    )
);

理论上,它只对那些PropA值与我们正在查看的对象相同的元素进行过滤。应该只有其中一个(本身)。

您可以对B进行类似的重复。

理论上将ForAll lambda表达式结合起来是微不足道的,但我不确定你是否愿意。当然,如果一个人做了而不是把它们全部混在一起并且知道某些事情失败但不是真正的事情,那么知道哪个条件失败会很好。

如果您可以对格式给予一点余地,可以尝试:

Contract.Requires(
    Contract.ForAll(
        coll.GroupBy(x=>x.PropA), 
        group => group.Count==1)
    )
);

这是一个类似的原则,但我认为会更有效地进行计数,因为group by和count将更有效率(我认为 - 我没有测试过,并且不熟悉linq方法的内部工作原理)。

另一种方法:

HashSet<object> propAValues = new HashSet<object>();
Contract.Requires(
    !coll.Any(x=>!hashset.Add(x.PropA))
);

这使用了一个hashset,如果该元素已经存在,则Add返回false。在这种情况下,Add生成一个false的时刻(因此lambda表达式为true)然后Any将返回true,因为它被否定将导致测试失败。

这种方法是否合理可能取决于你的对象有多大(因此对你的对象集加倍可能会产生内存影响。然而,与其他方法相比,它会终止最少的迭代次数(因为其他方法)方法需要查看集合中的每个对象,可能需要多次查看,而最后一个可能会在查看两个条目后停止。)