框架中是否有任何内置的集合类型(IEnumerable<S>
)或IEnumerable<S>
IEqualityComparer<T>
,该集合类型Equals
(并相应地GetHashCode
)由其中项的相等性定义?
像这样:
var x = new SomeCollection { 1, 2, 3 };
var y = new SomeCollection { 1, 2, 3 };
// so that x.Equals(y) -> true
// and x.Shuffle().Equals(y) -> false
或一个
class SomeComparer<T> : EqalityComparer<IEnumerable<T>> { }
// so that for
var x = new[] { 1, 2, 3 };
var y = new[] { 1, 2, 3 };
// gives
// new SomeComparer<int>().Equals(x, y) -> true
// new SomeComparer<int>().Equals(x.Shuffle(), y) -> false
?我的问题是,框架中是否有某些东西的行为类似于代码中显示的SomeCollection
或SomeComparer<T>
?
我为什么需要它:因为我有一个Dictionary<Collection, T>
案例,其中Key
部分应该是一个集合,它的平等性基于它的条目。
要求:
- 集合只需要是具有
Add
方法的简单可枚举类型 - 项目的顺序很重要
- 集合中可以存在重复项
注意:我可以自己写一个,这是微不足道的。有很多关于SO的问题可以帮助解决这个问题。我问框架本身是否有一个类。
保持简单。只需使用字典 ctor 它采用专门的 IEqualityComparer(只需在比较器中实现您的相等逻辑),您就可以开始了。无需特殊的收藏类型等...
看这里
可以,最好定义自己的不可变集合类,该集合类接受IEqualityComparer<T>
作为构造函数参数,并将其Equals
和GetHashCode()
成员链接到基础集合的成员,而不是尝试为此目的定义IEqualityComparer<T>
。 除此之外,不可变集合类将能够缓存自己的哈希值,并可能缓存其中包含的项的哈希值。 这不仅会加快对馆藏GetHashCode()
的调用,还会加快两个馆藏之间的比较。 如果两个集合的哈希码不相等,则没有必要进一步检查任何内容;即使两个集合的哈希码相等,在测试项目本身的相等性之前检查相应项的哈希码是否匹配可能是值得的 [请注意,一般来说,在检查相等性之前使用哈希码测试作为早期退出并不是特别有用,因为最慢的Equals
情况(项目匹配)是哈希代码无论如何都会匹配的情况;这里, 但是,如果除最后一个项目之外的所有项目都匹配,则测试项目的哈希代码可能会在花时间详细检查每个项目之前发现不匹配。
从 .NET 4.0 开始,可以使用ConditionalWeakTable
将集合映射到将缓存有关它们的信息的对象,编写一个IEqualityComparer<T>
来实现缓存哈希值的不可变集合类的性能优势。 尽管如此,除非无法使用自定义不可变集合类,否则我认为在这种情况下,这样的类可能比IEqualityComparer<T>
更好。
我不相信这样的事情存在。我需要比较两本字典的内容是否相等,并在不久前写了这篇文章。
public class DictionaryComparer<TKey, TValue> : EqualityComparer<IDictionary<TKey, TValue>>
{
public DictionaryComparer()
{
}
public override bool Equals(IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
{
// early-exit checks
if (object.ReferenceEquals(x, y))
return true;
if (null == x || y == null)
return false;
if (x.Count != y.Count)
return false;
// check keys are the same
foreach (TKey k in x.Keys)
if (!y.ContainsKey(k))
return false;
// check values are the same
foreach (TKey k in x.Keys)
{
TValue v = x[k];
if (object.ReferenceEquals(v, null))
return object.ReferenceEquals(y[k], null);
if (!v.Equals(y[k]))
return false;
}
return true;
}
public override int GetHashCode(IDictionary<TKey, TValue> obj)
{
if (obj == null)
return 0;
int hash = 0;
foreach (KeyValuePair<TKey, TValue> pair in obj)
{
int key = pair.Key.GetHashCode(); // key cannot be null
int value = pair.Value != null ? pair.Value.GetHashCode() : 0;
hash ^= ShiftAndWrap(key, 2) ^ value;
}
return hash;
}
private static int ShiftAndWrap(int value, int positions)
{
positions = positions & 0x1F;
// Save the existing bit pattern, but interpret it as an unsigned integer.
uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
// Preserve the bits to be discarded.
uint wrapped = number >> (32 - positions);
// Shift and wrap the discarded bits.
return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
}
}