【问题标题】:Code does not seem to pass a certain line, causing StackOverflowException代码似乎没有通过某行,导致 StackOverflowException
【发布时间】:2014-10-31 22:31:46
【问题描述】:

我有一小段代码比较 2 个变量的类型,然后如果它们相等或不相等则返回某个输出。代码如下:

public new bool Equals(object x, object y)
    {
        var xType = x.GetType();
        var yType = y.GetType();
        if (xType != yType) return false;

        return GetPropertiesWithoutKey(xType).All(pd => Comparer.IsPropertyChanged(pd, x, y));
    }

在调试时,我在'return GetPropertiesWithoutKey(...)' 上设置了一个断点,但是在它到达该行之后它不会越过它。即使在多次击中 F10 和 F5 之后。删除断点并按 F10 或 F5 后,我得到了 StackOverflow 异常。

有人知道为什么会这样吗?

欢迎提出问题、评论和建议。

亲切的问候

编辑 1:GetPropertiesWithoutKey(...) 的代码

private IList<PropertyDescriptor> GetPropertiesWithoutKey(Type entityType)
    {
        return _syncProperties ??
               (_syncProperties = GetEntityTypeDescriptor(entityType).Properties.Where(el => !el.IsKey).ToList());
    }

编辑 2:Comparer.IsPropertyChanged(...) 的代码

public static bool IsPropertyChanged(PropertyDescriptor pd, object entity1, object entity2)
    {
        object val1 = null;
        object val2 = null;
        if ((typeof(ICollection).IsAssignableFrom(pd.PropertyInfo.PropertyType)
            && entity1 != null
            && ((ICollection)entity1).Count != 0)
            || (!typeof(ICollection).IsAssignableFrom(pd.PropertyInfo.PropertyType)
            && entity1 != null))
        {
            val1 = entity1;
            //val1 = pd.PropertyInfo.GetValue(entity1);
        }

        if ((typeof(ICollection).IsAssignableFrom(pd.PropertyInfo.PropertyType)
             && entity2 != null
             && ((ICollection)entity2).Count != 0)
             || (!typeof(ICollection).IsAssignableFrom(pd.PropertyInfo.PropertyType)
             && entity2 != null))
        {
            val2 = entity2;
            //val2 = pd.PropertyInfo.GetValue(entity2);
        }

        if (val1 == null && val2 == null)
        {
            return false;
        }
        if (val1 == null || val2 == null)
        {
            return true;
        }
        if (typeof(ICollection).IsAssignableFrom(pd.PropertyInfo.PropertyType))
        {
            return IsCollectionChanged(pd, val1, val2);
        }
        if (!pd.PropertyInfo.PropertyType.IsPrimitive || !(pd.PropertyInfo.PropertyType == typeof(String)))
        {
            return IsComplexChanged(pd, val1, val2); //<-- Debugger gets here (EDIT 3)
        }

        if (val1 != null)
        {
            return !val1.Equals(val2);
        }
        else if (val2 != null)
        {
            return !val2.Equals(val1);
        }
        return false;
    }

编辑 3:IsComplexChanged 的​​代码

private static bool IsComplexChanged(PropertyDescriptor pd, object val1, object val2)
    {
        var comparer = new EqualityComparer();
        return !comparer.Equals(val1, val2); //<-- This line refers back to Equals function
    }

正如您在上次编辑中看到的那样,我发现了循环的制作位置。

首先,函数 GetChangedProperties() 调用 Comparer.IsPropertyChanged,在 IsPropertyChanged 调试器中始终会到达 Comparer.IsComplexChanged,这指的是 Equals。 在 GetPropertiesWithoutKey(...) 中 Equals 的 lambda 表达式中,调用了 Comparer.IsPropertyChanged,然后创建循环。我已经写下了所采取步骤的时间表。

如果有人想看这个,尽管问,我会给它拍照。

我希望有人知道解决方案或可以帮助我开始解决问题,非常感谢您的帮助,因为我现在正在画一个空白。

欢迎任何进一步的问题

【问题讨论】:

  • 发布GetPropertiesWithoutKey 的代码,它可能正在进行某种无限递归。
  • 或 Comparer.IsPropertyChanged 使用调用它的 Equals 方法。
  • 在许多编码语言中,Equals() 是一种现有方法。所以也许你覆盖了那个方法并继续调用它。尝试更改方法的名称。
  • 它似乎不是它不会越过“GetPropertiesWithoutKey”行,而是你一次又一次地回到那里,可能是因为某些东西正在回调你的 equals 方法。使用断点按 F5 几次后,您应该能够查看堆栈跟踪并查看正在发生的循环。它可能是一个大循环或一个小循环。一旦你有了它,你就有希望找出什么是回调到你的 equals 并修复该代码。或者未能发布堆栈跟踪以供我们帮助调试。
  • 嘿@Chris,我发现了循环的创建位置,我已将代码作为编辑放入问题中,以便每个人都可以清楚地看到。但是我的工作日快结束了,所以你可能要到早上才能再次收到我的消息:p

标签: c# linq


【解决方案1】:

Equals 方法(或以后的 IsPropertyChanged)内部,您不能使用 Equals 或任何 变体(==,!=) (因为它循环返回并导致堆栈溢出),但在这种情况下,您只需要 与null 比较,您不需要(我能想到的)比较 两个对象(你并不真的需要你的自定义 Equals)。因此对于 在这些情况下,只需使用 object.ReferenceEquals - 它不会触发 关闭您的自定义 Equals(它使用完全不同的路径)。

您只需要处理 entity != null 类型的检查(内部) - 通常使用 object.ReferenceEquals 解决,例如

!object.ReferenceEquals(entity1, null)  

而不是
entity1 != null

【讨论】:

  • 能否详细说明一下,我不太明白你的意思
  • 抱歉,这只是一个快速提示 - 在 IsPropertyChanged 方法内部,您不能使用 Equals(因为它会循环返回并导致堆栈溢出),但您只能需要与null 进行比较,您不需要(通常)比较这两个对象(并且您实际上并不需要您的自定义比较)。因此,对于这些情况,只需使用 object.ReferenceEquals - 它不会触发您的自定义比较(它使用完全不同的路径)。
【解决方案2】:

我通过向 IsPropertyChangedMethod 添加深度参数解决了该错误。现在错误不再发生,但我不确定我的代码现在是否有效,所以我会得出这个结论。

现在,以下是我为解决错误所做的更改:

IsPropertyChanged:

public static bool IsPropertyChanged(PropertyDescriptor pd, object entity1, object entity2, int depth)
    {
        if (depth > 3) return false;
        ...
        ...
        if (!pd.PropertyInfo.PropertyType.IsPrimitive || !(pd.PropertyInfo.PropertyType == typeof(String)))
        {
            return IsComplexChanged(pd, val1, val2, depth);
        }
        ...
        ...
    }

添加参数并将其传递给 IsComplexChanged

IsComplexChanged:

private static bool IsComplexChanged(PropertyDescriptor pd, object val1, object val2, int depth)
    {
        var comparer = new ConexioEqualityComparer();
        return !comparer.Equals(val1, val2, depth);
    }

再次将参数向下传递给 Equals

等于:

public bool Equals(object x, object y, int depth)
    {
        var xType = x.GetType();
        var yType = y.GetType();
        if (xType != yType) return false;

        return GetPropertiesWithoutKey(xType).All(pd => ConexioComparer.IsPropertyChanged(pd, x, y, ++depth));
    }

添加增量并返回 IsPropertyChanged

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-08-01
    • 2012-11-15
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多