使变量最后一个调用堆栈 [英] make a variable last for a call stack

查看:72
本文介绍了使变量最后一个调用堆栈的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含一些字段的类。我需要按值比较此类的实例,因此我分别定义了 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).

问题:


  1. 将使用线程静态变量工作吗?恐怕不会,因为我可以想象来自同一调用堆栈的不同 Equals 调用仍然可以在不同的线程上执行(但我不知道)。

  1. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆