gpt4 book ai didi

c# - 具有可为空字段的 EqualityComparer 的奇怪行为

转载 作者:行者123 更新时间:2023-12-04 00:58:07 26 4
gpt4 key购买 nike

假设有这个类:

public class Foo
{
public int Id { get; set; }
public int? NullableId { get; set; }

public Foo(int id, int? nullableId)
{
Id = id;
NullableId = nullableId;
}
}

我需要按照以下规则比较这些对象:

  1. 如果两个对象都具有 NullableId 值,那么我们比较两个 Id和 NullableId
  2. 如果某些对象/它们都没有 NullableId 那么忽略它,只比较 Id。

为了实现它,我像这样重写了 Equals 和 GetHashCode:

public override bool Equals(object obj)
{
var otherFoo = (Foo)obj;

var equalityCondition = Id == otherFoo.Id;

if (NullableId.HasValue && otherFoo.NullableId.HasValue)
equalityCondition &= (NullableId== otherFoo.NullableId);

return equalityCondition;
}

public override int GetHashCode()
{
var hashCode = 806340729;
hashCode = hashCode * -1521134295 + Id.GetHashCode();
return hashCode;
}

再往下我有两个 Foo 列表:

var first = new List<Foo> { new Foo(1, null) };
var second = new List<Foo> { new Foo(1, 1), new Foo(1, 2), new Foo(1, 3) };

接下来,我要加入这些列表。如果我这样做:

var result = second.Join(first, s => s, f => f, (f, s) => new {f, s}).ToList();

然后结果如我所料,我将得到 3 个项目。但是,如果我改变顺序并首先加入第二个:

var result = first.Join(second, f => f, s => s, (f, s) => new {f, s}).ToList();

那么结果将只有 1 个项目 - new Foo(1, null)new Foo(1 ,3)

我不明白我做错了什么。如果尝试在 Equals 方法中放置一个断点,那么我可以看到它尝试比较来自同一列表的项目(例如比较 new Foo(1, 1)new Foo(1 ,2 ))。对我来说,这似乎是因为在 Join 方法中创建了 Lookup。

有人可以澄清那里发生了什么吗?我应该更改什么以实现所需的行为?

最佳答案

您的 Equals 方法是自反和对称的,但它不是可传递的。

您的实现不符合文档中指定的要求:

If (x.Equals(y) && y.Equals(z)) returns true, then x.Equals(z) returns true.

来自 https://learn.microsoft.com/en-us/dotnet/api/system.object.equals?view=netframework-4.8

例如,假设您有:

var x = new Foo(1, 100);
var y = new Foo(1, null);
var z = new Foo(1, 200);

你有 x.Equals(y)y.Equals(z) 这意味着你也应该有 x.Equals(z),但您的实现不会这样做。由于您不符合规范,因此您不能指望任何依赖于您的 Equals 方法的算法都能正确运行。


你问的是你能做什么。这完全取决于您需要做什么。部分问题在于,如果它们确实可以出现,那么在极端案例中的意图并不十分清楚。如果一个 Id 在一个或两个列表中多次出现相同的 NullableId 会发生什么?举个简单的例子,如果 new Foo(1, 1) 在第一个列表中出现三次,在第二个列表中出现三次,那么输出中应该是什么?九个项目,每个配对一个?

这是解决您的问题的幼稚尝试。这仅加入 Id,然后过滤掉任何具有不兼容的 NullableId 的配对。但是当一个 Id 在每个列表中出现多次时,您可能不会想到重复项,如示例输出中所示。

using System;
using System.Linq;
using System.Collections.Generic;

public class Foo
{
public int Id { get; set; }
public int? NullableId { get; set; }

public Foo(int id, int? nullableId)
{
Id = id;
NullableId = nullableId;
}

public override string ToString() => $"Foo({Id}, {NullableId?.ToString()??"null"})";
}

class MainClass {
public static IEnumerable<Foo> JoinFoos(IEnumerable<Foo> first, IEnumerable<Foo> second) {
return first
.Join(second, f=>f.Id, s=>s.Id, (f,s) => new {f,s})
.Where(fs =>
fs.f.NullableId == null ||
fs.s.NullableId == null ||
fs.f.NullableId == fs.s.NullableId)
.Select(fs => new Foo(fs.f.Id, fs.f.NullableId ?? fs.s.NullableId));
}
public static void Main (string[] args) {
var first = new List<Foo> { new Foo(1, null), new Foo(1, null), new Foo(1, 3) };
var second = new List<Foo> { new Foo(1, 1), new Foo(1, 2), new Foo(1, 3), new Foo(1, null) };
foreach (var f in JoinFoos(first, second)) {
Console.WriteLine(f);
}
}
}

输出:

Foo(1, 1)
Foo(1, 2)
Foo(1, 3)
Foo(1, null)
Foo(1, 1)
Foo(1, 2)
Foo(1, 3)
Foo(1, null)
Foo(1, 3)
Foo(1, 3)

如果您有数万个具有相同 Id 的项目,它对您来说也可能太慢了,因为它会在过滤之前构建所有可能的具有匹配 Id 的对他们出来。如果每个列表有 10,000 个带有 Id == 1 的项目,那么就有 100,000,000 对要挑选。

关于c# - 具有可为空字段的 EqualityComparer 的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60696070/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com