C# - Lambda引发System.StackOverflowException异常

8
在什么情况下,此代码会出现 System.StackOverflowException 错误?
Accounts.Sort((x, y) => string.Compare(x.AccountId, y.AccountId));

更新:
该属性应写为:

    public string AccountId
    {
        get { return _accountId; }
        set { _accountId = value; }
    }

没有什么特别的事情发生。排序也没有被覆盖。


Accounts的类型是什么?List<string> - BlueRaja - Danny Pflughoeft
Accounts里有多少个项目? - Peter McG
关于Accounts,大约有10个对象。账户的类型是List<Account>。 - BuddyJoe
你确定是这一行引发了StackOverflowException吗? - Dan Tao
Account类是否重写了Equals()方法或定义了运算符==? - Peter McG
显示剩余2条评论
6个回答

5

我遇到了一个棘手的问题,我的比较方法出现了StackOverflow异常。

我的比较方法:

public bool Equals(Type rhs)
{
    if (rhs == null) return false;
    if (this == rhs) return true;

    return this.randomField.Equals(rhs.randomField);
}

我的运营商:

public static bool operator ==(Type lhs, Type rhs)
{
    if (lhs == null)
        return (rhs == null);
    else
        return lhs.Equals(rhs);
}

所以,发生的事情是 == 操作符调用了 Equals 方法,然后在运行 this == rhs 时调用了 == 操作符。解决方法是将该行转换为 Object.ReferenceEquals(this, rhs)


问题编辑是在我开始写帖子之后进行的。显然,字符串不会有这样的问题。 - jdmichal

5
看一下调用堆栈,你会发现哪个函数一遍又一遍地执行。如果这不可能(例如在生产环境中运行),请提供更多信息,例如被调用的属性调用了什么,这个函数在哪里被调用等等。

5
如果AccountId执行的操作比较复杂(即访问本地字段以外的任何内容),那么这就是最有可能出现问题的地方。
有趣的是,技术上来说,Sort需要排序具有可传递性,而字符串比较并不总是可传递的!但这很少会导致stackoverflow错误(除非Sort方法使用某种FP方法);它可能会导致程序运行时间无限延长,但我相信内置类型已经考虑到了这一点(它们检查理论上的最大运行长度并中止,如果我没记错的话)。
我会看一下AccountId,如果它执行了一些“聪明”的操作(比如从父集合中惰性加载某个值),那么问题很可能就出在这里。

6
在 .Net 4.0 中,看起来已经修复了这个及物 Bug。 - BlueRaja - Danny Pflughoeft

4

这是一个奇特的想法:

账户是否被声明为List<Account>

我在想,如果Accounts属性被声明为其他类型,例如IList<Account>,并且您在某个地方有一个静态的帮助类,其中Sort扩展方法未正确实现。当传入的参数是List<T>时,它可能会尝试利用List<T>.Sort方法,但却没有执行必要的转换为List<T>,从而导致StackOverflowException

我的意思是这样的。假设Account是某个类的属性,该类看起来像这样:

public class AccountManager
{
    public IList<Account> Accounts { get; private set; }

    public AccountManager()
    {
        // here in the constructor, Accounts is SET to a List<Account>;
        // however, code that interacts with the Accounts property will
        // only know that it's interacting with something that implements
        // IList<Account>
        Accounts = new List<Account>();
    }
}

假设您在其他地方有一个静态类,其中包含 Sort 扩展方法:

public static class ListHelper
{
    public static void Sort<T>(this IList<T> list, Comparison<T> comparison)
    {
        // programmer tries to use the built-in sort, if possible
        if (list is List<T>)
        {
            // only problem is, list is here still typed as IList<T>,
            // so this line will result in infinite recursion
            list.Sort(comparison);

            // the CORRECT way to have done this would've been:
            // ((List<T>)list).Sort(comparison);

            return;
        }
        else
        {
            list.CustomSort(comparison);
            return;
        }
    }

    private static void CustomSort<T>(this IList<T> list, Comparison<T> comparison)
    {
        // some custom implementation
    }
}

在这种情况下,您发布的代码将抛出一个StackOverflowException异常。

原始回答:

也许Accounts是自定义集合类的对象,其Sort方法调用了自身?

public class AccountCollection : IEnumerable<Account> {
    // ...
    public void Sort(Comparison<Account> comparison) {
        Sort(comparison); // infinite recursion
    }
    // ...
}

也许 AccountId 属性会调用自身?
public class Account {
    // ...
    public string AccountId {
        get { return AccountId; } // infinite recursion
    }
    // ...
}

2

仅仅因为这一行代码引发了StackOverflow错误,并不意味着它就是问题的根源,例如:

void methodA()
{
   b();
   methodA();
}

这段代码在调用b()和methodA()时都有可能导致堆栈溢出;

我猜测递归发生在这行代码附近的方法中。


2

StackOverflowException通常发生在递归调用失控时。请检查 SortAccountId 是否在调用自身。如果是,请检查这些递归函数的基本情况,并确保它们在应该停止时停止。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接