小结
我想建立一套套在C#项目。 项目的内套有一个GetHashCode
和Equals
通过它们的内容定义的方法。 在数学符号:
x = { }
x.Add( { A, B, C } )
x.Add( { A, D } )
x.Add( { B, C, A } )
now x should be{ { A, B, C }, { A, D } }
在Python中,这可能是与完成frozenset
:
x = set()
x.add( frozenset(['A','B','C']) )
x.add( frozenset(['A','D']) )
x.add( frozenset(['B','C','A']) )
/ BriefSummary
我想在C#中的哈希的HashSet的。 这将允许我这样做:
HashSet<ContentHashableHashSet<int>> setOfSets;
虽然有更复杂的方式来做到这一点,这可以在平凡的做法(虽然不是最有效的方式)加入重写实现ContentHashableHashSet.ToString()
(outputing包含在排序顺序元素的字符串),然后再使用使用ContentHashableHashSet.ToString().GetHashCode()
作为哈希码。
然而,如果一个对象在放置后修改setOfSets
,它可能会导致多个副本:
var setA = new ContentHashableHashSet<int>();
setA.Add(1);
setA.Add(2);
var setB = new ContentHashableHashSet<int>();
setB.Add(1);
setOfSets.Add(setA);
setOfSets.Add(setB);
setB.Add(2); // now there are duplicate members!
据我所看到的,我有两个选择:我可以得出ContentHashableHashSet
从HashSet
,但后来我需要让这个所有的调节器抛出异常。 缺少一种调节剂可能会导致一个阴险的bug。
或者,我可以使用封装和类ContentHashableHashSet
可以包含一个readonly HashSet
。 但后来,我需要重新实现所有设置方法(除了修饰),以便ContentHashableHashSet
可以表现得像一个HashSet
。 据我所知,扩展将不适用。
最后,我可以如上封装,然后所有的设置一样的功能将通过返回的常量发生(或只读?)HashSet的成员。
事后看来,这让人想起了Python的的frozenset
。 有谁知道一个精心设计的方式在C#来实现这一点?
如果我能失去ISet
功能,那么我会简单地创建一个有序ImmutableList
,但后来我想快结合,快速路口和子线(大约为O(log(n))的)设置会员与丧失功能Contains
。
编辑:基类的HashSet 不具有虚拟Add
和Remove
的方法,所以它们覆盖将派生类内工作,但如果执行将无法正常工作HashSet<int> set = new ContentHashableHashSet<int>();
。 铸造基类将允许编辑。
编辑2:感谢@xanatos用于推荐简单GetHashCode
实现:
计算GetHashCode的最简单的方法是简单地异或(^)元素的所有gethashcodes。 XOR运算符是可交换的,所以排序是无关紧要的。 为了比较,你可以使用SetEquals
编辑3:最近有人分享有关信息ImmutableHashSet ,但由于这个类是密封的,这是不可能从中派生并重写GetHashCode
。
我还被告知, HashSet
接受一个IEqualityComparer
作为参数,所以这可以被用来提供一个不可变的,内容哈希的集合,而不从ImmutableHashSet获得; 然而,这是不是一个非常面向对象的解决方案:我想使用每次ContentHashableHashSet
,我就需要通过相同的(非平凡)的说法。 正如我敢肯定,你知道,这真的可以肆虐你的编码禅宗,并在那里我会沿着蟒蛇同飞myDictionary[ frozenset(mySet) ] = myValue
,我会被卡住做同样的事情一而再,再而再次 。
感谢您的任何帮助,您可以提供。 我有一个临时的解决方法(其问题在上面编辑1被提及),但我大多喜欢了解设计这样的事情的最好方法。