我正在尝试编写一个Linq查询,该查询返回一个对象数组,其构造函数中包含唯一值。对于整数类型,Distinct只返回每个值的一个副本,但是当我尝试创建对象列表时,事情就会崩溃。我怀疑这是我班级的相等运算符的问题,但是当我设置断点时,它从未被击中。
在子表达式中过滤掉重复的int可以解决问题,并且还可以避免构造将立即丢弃的对象,但我很好奇为什么这个版本不起作用。
更新:晚上11:04 有几个人指出MyType不会覆盖GetHashCode()。我担心这个例子过于简单了。原始的MyType确实实现了它。我在下面添加了它,仅修改为在返回它之前将哈希码放入临时变量中。
通过调试器,我看到GetHashCode的所有五次调用都返回不同的值。由于MyType只从Object继承,因此可能与Object表现出相同的行为。
我是否正确然后得出结论,哈希应该基于值的内容?这是我第一次尝试覆盖运营商,当时看起来GetHashCode似乎并不特别花哨。 (这是我的第一次平等检查似乎无法正常工作。)
class Program
{
static void Main(string[] args)
{
int[] list = { 1, 3, 4, 4, 5 };
int[] list2 =
(from value in list
select value).Distinct().ToArray(); // One copy of each value.
MyType[] distinct =
(from value in list
select new MyType(value)).Distinct().ToArray(); // Two objects created with 4.
Array.ForEach(distinct, value => Console.WriteLine(value));
}
}
class MyType
{
public int Value { get; private set; }
public MyType(int arg)
{
Value = arg;
}
public override int GetHashCode()
{
int retval = base.GetHashCode();
return retval;
}
public override bool Equals(object obj)
{
if (obj == null)
return false;
MyType rhs = obj as MyType;
if ((Object)rhs == null)
return false;
return this == rhs;
}
public static bool operator ==(MyType lhs, MyType rhs)
{
bool result;
if ((Object)lhs != null && (Object)rhs != null)
result = lhs.Value == rhs.Value;
else
result = (Object)lhs == (Object)rhs;
return result;
}
public static bool operator !=(MyType lhs, MyType rhs)
{
return !(lhs == rhs);
}
}
答案 0 :(得分:8)
您需要在类中重写GetHashCode()。 GetHashCode必须与Equals重载一起实现。代码在调用Equals之前检查哈希码是否相等是很常见的。这就是为什么你的Equals实现没有被调用。
答案 1 :(得分:2)
您的怀疑是正确的,它是当前只检查对象引用的相等。即使你的实现没有做任何额外的事情,改为:
public override bool Equals(object obj)
{
if (obj == null)
return false;
MyType rhs = obj as MyType;
if ((Object)rhs == null)
return false;
return this.Value == rhs.Value;
}
答案 2 :(得分:2)
在你的平等方法中,你仍在测试引用相等性,而不是语义相等性,例如在这一行:
result = (Object)lhs == (Object)rhs
您只是比较两个对象引用,即使它们拥有完全相同的数据,它们仍然不是同一个对象。相反,您的相等性测试需要比较对象的一个或多个属性。例如,如果您的对象具有ID属性,并且具有相同ID的对象应被视为在语义上等效,那么您可以这样做:
result = lhs.ID == rhs.ID
请注意,重写Equals()意味着你还应该覆盖GetHashCode(),这是另一个鱼的水壶,并且可能很难正确完成。
答案 3 :(得分:1)
您需要实现GetHashCode()。
答案 4 :(得分:1)
似乎可以更加优雅地实现简单的Distinct操作,如下所示:
var distinct = items.GroupBy(x => x.ID).Select(x => x.First());
其中,ID是确定两个对象在语义上是否等效的属性。从这里的混乱(包括我自己)来看,Distinct()的默认实现似乎有点复杂。
答案 5 :(得分:0)
我认为MyType需要实现IEquatable才能实现此目的。
答案 6 :(得分:0)
其他答案几乎涵盖了您需要正确实现Equals和GetHashCode这一事实,但作为旁注,您可能有兴趣知道匿名类型会自动实现这些值:
var distinct =
(from value in list
select new {Value = value}).Distinct().ToArray();
因此,无需定义此类,您将自动获得您正在寻找的Equals和GetHashCode行为。很酷,嗯?