我有一个包含一些字段的类。我需要按值比较此类的实例,所以我定义了GetHashCode
and Equals
因此。因为该类允许循环引用,所以我需要一种机制来避免无限递归(更详细的解释请参见值等于和循环引用:如何解决无限递归? https://stackoverflow.com/questions/46586427/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)
in checkedPairs
在它调用之前foo2.Equals(foo1)
。里面foo2.Equals(foo1)
将会注意到checkedPairs
包含(foo1,foo2)
, and true
将被退回。该结果被传送到equal
调用中的变量foo1.Equals(foo2)
, then checkedPairs
被清除,并且true
最终返回到main方法。
(不利用checkedPairs
inside Equals
,之间会有无限递归跳跃foo1.Equals(foo2)
and foo2.Equals(foo1)
.)
这在我的单线程、非并发沙箱环境中运行得很好。然而,我只使用一个static
字段为checkedPairs
因为我不知道有任何其他方法可以从一次调用中转移已经收集的物品Equals
到调用堆栈中的下一个。
但通过这种方法,我无法使用多线程或并发环境,其中多个Equals
检查可能会并行运行或以混合顺序运行(例如,由于通过Equals
作为委托并稍后而不是立即调用它)。
问题:
使用线程静态变量可以吗?恐怕不是,因为我可以想象到不同的情况Equals
来自同一调用堆栈的调用仍然可以在不同的线程上执行(但我不知道)。
有没有办法制作checkedPairs
“调用堆栈静态”?这样每个调用堆栈都有自己的副本checkedPairs
?然后对于每个新的调用堆栈,一个新的(空)checkedPairs
将在递归期间创建、填充,并在递归结束后进行垃圾收集。