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

m-y*_*m-y 5 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; }
}
Run Code Online (Sandbox Code Playgroud)

要求是:

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

似乎无法弄清楚我的Contract.ForAll(...)声明在这里做什么.


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

Chr*_*ris 1

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

Contract.Requires(
    Contract.ForAll(
        coll, 
        obj => (coll.Where(x=>x.PropA = obj.PropA).Count==1)
    )
);
Run Code Online (Sandbox Code Playgroud)

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

您可以对 B 重复类似的操作。

理论上,组合 ForAll lambda 表达式是微不足道的,但我不确定您是否愿意这样做。当然,如果一个人失败了,知道哪种情况失败了,而不是把它们全部放在一起,只知道某件事失败了,但不知道到底是什么失败了,那就太好了……

如果您可以在格式上留出一点余地,您可以尝试:

Contract.Requires(
    Contract.ForAll(
        coll.GroupBy(x=>x.PropA), 
        group => group.Count==1)
    )
);
Run Code Online (Sandbox Code Playgroud)

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

另一种方法:

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

这使用了哈希集,并且如果元素已存在,则 Add 返回 false。在这种情况下,当 Add 生成 false(因此 lambda 表达式为 true)时,Any 将返回 true,因为它被否定,因此测试将失败。

此方法是否明智可能取决于您的对象有多大(以及将对象集加倍的潜在内存影响)。然而,与此处的其他方法相比,它需要最少的迭代来终止(因为其他方法需要查看集合中的每个对象,可能会多次,而最后一个对象可能会在查看两个条目后停止)。