我有一个包含一些字段的类。我需要按值比较此类的实例,因此我相应地定义了GetHashCode
和Equals
。因为类允许循环引用,所以我需要一种机制来避免无限递归(有关更详细的解释,请参阅Value-equals and circular references: how to resolve infinite recursion?)。我通过修改我的Equals
方法解决了这个问题,以便跟踪之前完成的比较:
class Foo
{
public string Name { get; set; }
public Foo Reference { get; set; }
public override int GetHashCode() { return Name.GetHashCode(); }
static HashSet<(Foo,Foo)> checkedPairs
= new HashSet<(Foo,Foo)>(ValuePairRefEqualityComparer<Foo>.Instance);
// using an equality comparer that compares corresponding items for reference;
// implementation here: https://stackoverflow.com/a/46589154/5333340
public override bool Equals(object obj)
{
Foo other = obj as Foo;
if (other == null)
return false;
if !(Name.Equals(other.Name))
return false;
if (checkedPairs.Contains((this,other)) || checkedPairs.Contains((other,this)))
return true;
checkedPairs.Add((this,other));
bool refsEqual = Reference.Equals(other.Reference);
checkedPairs.Clear();
return refsEqual;
}
}
想象一下main方法中的以下代码:
Foo foo1 = new Foo { Name = "foo" };
Foo foo2 = new Foo { Name = "foo" };
foo1.Reference = foo2;
foo2.Reference = foo1;
bool foo_equals_bar = foo1.Equals(foo2);
Console.WriteLine("foo_equals_bar = " + foo_equals_bar);
foo1.Equals(foo2)
会在调用(foo1,foo2)
之前将checkedPairs
存储在foo2.Equals(foo1)
中。在foo2.Equals(foo1)
内,我们会注意到checkedPairs
包含(foo1,foo2)
,并且会返回true
。此结果将转移到equal
调用中的foo1.Equals(foo2)
变量,然后checkedPairs
被清除,true
最终返回到main方法。
(在checkedPairs
内部不使用Equals
,foo1.Equals(foo2)
和foo2.Equals(foo1)
之间会有无限递归。)
这在我的单线程,非并发沙箱环境中可以正常工作。但是,我仅使用static
checkedPairs
字段,因为我不知道如何将已收集的项目从Equals
的一次调用转移到Equals
接下来是一个调用堆栈。
但是使用这种方法我不能使用多线程或并发环境,其中多个Equals
检查可以并行或以混合顺序运行(例如,由于将Equals
作为委托传递并在以后调用它而不是立即调用它。
问题:
使用线程静态变量会起作用吗?我不敢,因为我可以想象来自同一个调用堆栈的不同checkedPairs
调用仍然可以在不同的线程上执行(但我不知道)。
有没有办法让checkedPairs
&#34;调用堆栈静态&#34;?这样每个调用堆栈都有自己的checkedPairs
副本?然后,对于每个新的调用堆栈,将创建一个新的(空)perl -lane 'print join " ", grep {/\b[bcdfghjklmnpqrstvwxyz][[:alpha:]]*[aeiou]\b/i} @F' file
,在递归期间填充,并在递归结束后收集垃圾。
答案 0 :(得分:2)
感谢jdweng指出一个适用于问题中所述特定代码的简单解决方案:
从checkedPairs
类中删除Foo
字段,并使用以下代码替换Equals
方法:
public override bool Equals(object obj)
{
return MyEquals(obj, new HashSet<(Foo,Foo)>(ValuePairRefEqualityComparer<Foo>.Instance));
}
private bool MyEquals(object obj, HashSet<(Foo,Foo)> checkedPairs)
{
Foo other = obj as Foo;
if (other == null)
return false;
if (!Name.Equals(other.Name))
return false;
if (checkedPairs.Contains((this,other)) || checkedPairs.Contains((other,this)))
return true;
checkedPairs.Add((this,other));
return Reference.MyEquals(other.Reference, checkedItems);
}
但是,这种方法一般不会起作用。以此问题中的类为例:Value-equals and circular references: how to resolve infinite recursion?,并想象我为MyEquals
和Club
类似地定义了Person
。由于MyEquals
不能从类外部调用(我希望它是私有的),因此仍然会有无限递归。例如。调用Person.MyEquals
时,它会在内部调用FavouriteInstitution.Equals
,但它应该以某种方式重定向到FavouriteInstitution.MyEquals
(可能已填充checkedPairs
!)。此外,Members.SetEquals(other.Members)
会重定向到Person.Equals
而不是Person.MyEquals
。