我有一些包含几个字段的类。我需要通过值来比较它们,即如果一个类的两个实例包含相同的数据,则它们是相等的。我已经覆盖了GetHashCode
和Equals
方法。值等于和循环引用:如何解决无限递归?
可能会发生这些类包含循环引用。
例如:我们希望模拟机构(如政府,体育俱乐部等)。一个机构有一个名字。 A Club
是一个有名字和成员名单的机构。每个成员都有一个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可以拥有哪些价值。
就像你说的那样,一个无限递归可能发生在X长度的圆圈中(例如:在10次迭代之后,它指向第二个圆柱体,然后圆圈再次开始)。如果递归是从1导航到1,而不是1导航到很多,那么我会包含一个“访问节点”列表,并验证当前是否已经处理,如果是,则返回。 – Dryadwoods
正如上面建议的那样,通过/保存一个哈希集或其他已经被测试/被调用的引用。它可以是一个可选参数,设置为null,在第一次调用之后被创建并传递。 – Rob
在我的opionion中,你的'Equals'实现太严格了。为什么两个人只有拥有同一个喜爱的设备才是平等的?为什么一个人在这个周末去新俱乐部时会有所不同?一个简单的解决方法是引入一个'Id'属性 –