gpt4 book ai didi

c# - 值等于和循环引用 : how to resolve infinite recursion?

转载 作者:太空狗 更新时间:2023-10-29 20:32:59 24 4
gpt4 key购买 nike

我有一些包含多个字段的类。我需要按值比较它们,即,如果一个类的两个实例的字段包含相同的数据,则它们是相等的。为此,我覆盖了 GetHashCodeEquals 方法。

这些类可能包含循环引用。

示例:我们想要为机构建模(例如政府、体育俱乐部等)。一个机构有一个名字。 俱乐部 是一个具有名称和成员列表的机构。每个成员都是一个 Person,具有姓名和最喜欢的机构。如果某个俱乐部的成员将这个俱乐部作为他最喜欢的机构,我们就会有一个循环引用。

但是循环引用和值相等会导致无限递归。这是一个代码示例:

interface IInstitution { string Name { get; } }

class Club : IInstitution
{
public string Name { get; set; }
public HashSet<Person> Members { get; set; }

public override int GetHashCode() { return Name.GetHashCode() + Members.Count; }

public override bool Equals(object obj)
{
Club other = obj as Club;
if (other == null)
return false;

return Name.Equals(other.Name) && Members.SetEquals(other.Members);
}
}

class Person
{
public string Name { get; set; }
public IInstitution FavouriteInstitution { get; set; }

public override int GetHashCode() { return Name.GetHashCode(); }

public override bool Equals(object obj)
{
Person other = obj as Person;
if (other == null)
return false;

return Name.Equals(other.Name)
&& FavouriteInstitution.Equals(other.FavouriteInstitution);
}
}

class Program
{
public static void Main()
{
Club c1 = new Club { Name = "myClub", Members = new HashSet<Person>() };
Person p1 = new Person { Name = "Johnny", FavouriteInstitution = c1 }
c1.Members.Add(p1);

Club c2 = new Club { Name = "myClub", Members = new HashSet<Person>() };
Person p2 = new Person { Name = "Johnny", FavouriteInstitution = c2 }
c2.Members.Add(p2);

bool c1_and_c2_equal = c1.Equals(c2); // StackOverflowException!
// c1.Equals(c2) calls Members.SetEquals(other.Members)
// Members.SetEquals(other.Members) calls p1.Equals(p2)
// p1.Equals(p2) calls c1.Equals(c2)
}
}

c1_and_c2_equal应该返回true,而实际上我们(人类)稍加思考就可以看出它们是值相等的,而不会陷入无限递归。但是,我真的不能说我们是如何解决这个问题的。但既然可以,我希望也有办法在代码中解决这个问题!

所以问题是:如何在不遇到无限递归的情况下检查值是否相等?

请注意,我需要解决一般的循环引用,而不仅仅是上面的情况。因为 c1 引用 p1,而 p1 引用 c1,所以我将其称为 2 圆。可以有其他 n 圈,例如如果一个俱乐部 A 有一个成员 M,他最喜欢的俱乐部 B 有一个成员 N,他最喜欢的俱乐部是 A。那将是一个4圈。其他对象模型也可能允许具有奇数 n 的 n 圆。我正在寻找一种一次性解决所有这些问题的方法,因为我不会事先知道 n 可以有哪个值。

最佳答案

一个简单的解决方法(在 RDBMS 中使用)是使用唯一的 Id 来标识 Person(任何类型)。这样您就不需要比较所有其他属性,也永远不会遇到此类循环引用。

另一种方法是在 Equals 中进行不同的比较,因此只为 Equals 的类型提供深度检查,而不为引用的类型提供深度检查。您可以使用自定义比较器:

public class PersonNameComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
if (x == null && y == null) return true;
if (x == null || y == null) return false;
if(object.ReferenceEquals(x, y)) return true;
return x.Name == y.Name;
}

public int GetHashCode(Person obj)
{
return obj?.Name?.GetHashCode() ?? int.MinValue;
}
}

现在您可以更改 ClubEquals 实现,以避免 Members(Persons)使用包括机构在内的深度检查但只有他们的姓名:

public override bool Equals(object obj)
{
if (Object.ReferenceEquals(this, obj))
return true;

Club other = obj as Club;
if (other == null)
return false;

var personNameComparer = new PersonNameComparer();
return Name.Equals(other.Name)
&& Members.Count == other.Members.Count
&& !Members.Except(other.Members, personNameComparer).Any();
}

您注意到我不能使用 SetEquals,因为我的自定义比较器没有重载。

关于c# - 值等于和循环引用 : how to resolve infinite recursion?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46586427/

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