问题:
当我使用Contains()针对IEnumerable<T>的是正确地实现类IEquatable和覆盖GetHashCode返回false.如果我将匹配目标包装在列表中并执行,Intersect()则匹配正常.我更愿意使用Contains().
在IEnumerable.Contains()从MSDN:
使用默认的相等比较器将元素与指定值进行比较
在EqualityComparer<T>.Default物业从MSDN:
Default属性检查类型T是否实现System.IEquatable泛型接口,如果是,则返回使用该实现的EqualityComparer.否则返回一个EqualityComparer,它使用由T提供的Object.Equals和Object.GetHashCode的覆盖.
据我所知,IEquatable<T>在我的类上实现应该意味着Equals在尝试查找匹配时默认比较器使用该方法.我想使用Equals,因为我希望只有一种方式,两个对象是相同的,我不想要开发人员必须记住的策略.
我觉得奇怪的是,如果我将匹配目标包装在a中List然后执行a ,则Intersect匹配被正确找到.
我错过了什么?我是否必须创建一个相等比较器,如MSDN文章?MSDN建议有IEquatable足够的东西,它会包装给我.
控制台应用示例
NB:GetHashCode()来自Jon Skeet
using System;
using System.Collections.Generic;
using System.Linq;
namespace ContainsNotDoingWhatIThoughtItWould
{
class Program
{
public class MyEquatable : IEquatable<MyEquatable>
{
string[] tags;
public MyEquatable(params string[] tags)
{
this.tags = tags;
}
public bool Equals(MyEquatable other)
{
if (other == null)
{
return false;
}
if (this.tags.Count() != other.tags.Count())
{
return false;
}
var commonTags = this.tags.Intersect(other.tags);
return commonTags.Count() == this.tags.Count();
}
public override int GetHashCode()
{
int hash = 17;
foreach (string element in this.tags.OrderBy(x => x))
{
hash = unchecked(hash * element.GetHashCode());
}
return hash;
}
}
static void Main(string[] args)
{
// Two objects for the search list
var a = new MyEquatable("A");
var ab = new MyEquatable("A", "B");
IEnumerable<MyEquatable> myList = new MyEquatable[]
{
a,
ab
};
// This is the MyEquatable that we want to find
var target = new MyEquatable("A", "B");
// Check that the equality and hashing works
var isTrue1 = target.GetHashCode() == ab.GetHashCode();
var isTrue2 = target.Equals(ab);
var isFalse1 = target.GetHashCode() == a.GetHashCode();
var isFalse2 = target.Equals(a);
// Why is this false?
var whyIsThisFalse = myList.Contains(target);
// If that is false, why is this true?
var wrappedChildTarget = new List<MyEquatable> { target };
var thisIsTrue = myList.Intersect(wrappedChildTarget).Any();
}
}
}
Run Code Online (Sandbox Code Playgroud)
好的 - 问题实际上是在数组实现中ICollection<T>.Contains.你可以看到这样:
static void Main(string[] args)
{
var ab = new MyEquatable("A", "B");
var target = new MyEquatable("A", "B");
var array = new[] { ab };
Console.WriteLine(array.Contains(target)); // False
var list = new List<MyEquatable> { ab };
Console.WriteLine(list.Contains(target)); // True
var sequence = array.Select(x => x);
Console.WriteLine(sequence.Contains(target)); // True
}
Run Code Online (Sandbox Code Playgroud)
Enumerable.Contains委托ICollection<T>.Contains如果源实现ICollection<T>,这就是为什么你得到数组行为而不是代码中的Enumerable.Contains"长手"实现.
现在ICollection<T>.Contains 确实说实现选择使用哪个比较器:
实现可以在决定对象相等性方面有所不同; 例如,
List<T>使用Comparer<T>.Default,而Dictionary<TKey, TValue>允许用户指定IComparer<T>用于比较密钥的实现.
但:
EqualityComparer<T>而IEqualityComparer<T>不是Comparer<T>和IEqualityComparer<T>EqualityComparer<T>对我来说似乎非常不自然.该解决方案是重写object.Equals(object):
public override bool Equals(object other)
{
return Equals(other as MyEquatable);
}
Run Code Online (Sandbox Code Playgroud)
为了保持一致性,实现这两者IEquatable<T> 并覆盖它通常是令人愉快的object.Equals(object).因此,虽然您的代码应该在我的视图中工作
| 归档时间: |
|
| 查看次数: |
752 次 |
| 最近记录: |