使变量最后一个调用堆栈 [英] make a variable last for a call stack
问题描述
我有一个包含一些字段的类。我需要按值比较此类的实例,因此我分别定义了 GetHashCode
和 Equals
。因为该类允许循环引用,所以我需要一种避免无限递归的机制(有关更多详细说明,请参见值等于和循环引用:如何解决无限递归?)。我通过修改 Equals
方法解决了这个问题,以便跟踪以前进行的比较:
I have a class that contains some fields. I need to compare instances of this class by value, so I defined GetHashCode
and Equals
accordingly. Because the class allows circular references, I need a mechanism to avoid infinite recursion (for a more detailed explanation see Value-equals and circular references: how to resolve infinite recursion?). I solved this problem by modifying my Equals
method so that it keeps track of the comparisons done before:
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;
}
}
在主方法中想象以下代码:
Imagine the following code in the main method:
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
将被返回。此结果将转移到 foo1.Equals(foo2)
的调用中的 equal
变量中,然后是 checkedPairs
被清除,并且 true
最终返回到主方法。
foo1.Equals(foo2)
will store (foo1,foo2)
in checkedPairs
before it calls foo2.Equals(foo1)
. Inside foo2.Equals(foo1)
it will be noticed that checkedPairs
contains (foo1,foo2)
, and true
will be returned. This result is transferred to the equal
variable inside the call of foo1.Equals(foo2)
, then checkedPairs
is cleared, and true
is finally returned to the main method.
((如果不使用 Equals
内的 checkedPairs
,在 foo1之间将存在无限递归跳转。等于(foo2)
和 foo2。等于(foo1)
。)
(Without utilizing checkedPairs
inside Equals
, there would be an infinite recursion jumping between foo1.Equals(foo2)
and foo2.Equals(foo1)
.)
在我的单线程非并行沙箱环境中还可以。但是,我仅对 checkedPairs
使用静态
字段,因为我不知道其他任何方法将已收集的项目从一次 Equals
转移到呼叫堆栈中的下一个呼叫。
This works allright in my single-threaded, non-concurrent sandbox environment. However, I am only using a static
field for checkedPairs
because I don't know any other way to transfer the already collected items from one call of Equals
to the next inside a call stack.
但是,使用这种方法时,我不能使用多线程或并发环境,在该环境中,多个 Equals
检查可能并行或以混合顺序运行(例如,由于通过了等于
作为代理,以后再调用而不是立即调用。)
But with this approach I cannot use a multi-threaded or concurrent environment, where several Equals
checks might run in parallel or in a mixed-up order (e.g. due to passing Equals
as a delegate and invoking it later on instead of immediately).
问题:
-
将使用线程静态变量工作吗?恐怕不会,因为我可以想象来自同一调用堆栈的不同
Equals
调用仍然可以在不同的线程上执行(但我不知道)。
Will using a thread-static variable work? I am afraid not, because I can imagine that different
Equals
calls from the same call stack could still be executed on different threads (but I don't know).
是否可以使 checkedPairs
调用栈静态?这样每个调用堆栈都会得到自己的 checkedPairs
副本?然后,对于每个新的调用堆栈,将创建一个新的(空) checkedPairs
,在递归期间进行填充,并在递归结束后收集垃圾。
Is there a way to make checkedPairs
"call stack static"? So that each call stack gets its own copy of checkedPairs
? Then for each new call stack, a new (empty) checkedPairs
would be created, filled during recursion, and garbage collected after the recursion ends.
推荐答案
感谢jdweng为我指出一个简单的解决方案,该解决方案适用于问题中所述的特定代码:
Thanks jdweng to point me to an easy solution that works for the particular code stated in the question:
从 Foo
类中删除 checkedPairs
字段并替换 Equals
方法通过以下代码:
Remove the checkedPairs
field from the Foo
class and replace the Equals
method by this code:
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);
}
但是,这种方法通常不会起作用强>。以这个问题中的类为例:值等于和循环引用:如何解决无限递归?,并想像一下我为 Club
MyEquals >和 Person
。由于 MyEquals
不能从类外部调用(我希望将其私有),因此仍然存在无限递归。例如。调用 Person.MyEquals
时,它将在内部调用 FavouriteInstitution.Equals
,但应重定向到 FavouriteInstitution.MyEquals
(可能已经填充了 checkedPairs
Members.SetEquals(other.Members)将重定向到 Person.Equals
而不是 Person.MyEquals
。
However, this approach is not going to work in general. Take for example the classes from this question: Value-equals and circular references: how to resolve infinite recursion?, and imagine I defined MyEquals
analogously there for both Club
and Person
. Since MyEquals
cannot be called from outside the class (I want it private), there will still be infinite recursion. E.g. when Person.MyEquals
is called, it will call FavouriteInstitution.Equals
inside, but it should redirect to FavouriteInstitution.MyEquals
somehow(with a possibly already filled checkedPairs
!). Also, Members.SetEquals(other.Members)
will redirect to Person.Equals
instead of Person.MyEquals
.
这篇关于使变量最后一个调用堆栈的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!