【问题标题】:C# - System.StackOverflowException with LambdaC# - System.StackOverflowException 与 Lambda
【发布时间】:2011-02-16 13:56:02
【问题描述】:

在什么情况下这段代码会报错 System.StackOverflowException?

Accounts.Sort((x, y) => string.Compare(x.AccountId, y.AccountId));

更新
该属性写为:

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

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

【问题讨论】:

  • 什么是账户类型? List<string>?
  • 出于兴趣,帐户中有多少项目?
  • 帐户中有大约 10 个对象。并且帐户类型是 List
  • 你确定是 this 行抛出了StackOverflowException吗?
  • Account 类是否覆盖 Equals() 或定义运算符 ==

标签: c# .net lambda sorting stack-overflow


【解决方案1】:

查看调用堆栈,您将看到一遍又一遍地执行哪个函数。如果这不可能(例如因为它在生产环境中运行),请提供更多信息。

关于被调用的属性调用了什么,这个函数在哪里被调用,等等

【讨论】:

    【解决方案2】:

    如果AccountId 做了一些不平凡的事情(任何事情,除了访问本地字段),那么这是最有可能的选择。

    一个有趣的事实是技术上 Sort 要求排序是可传递的,而字符串比较是not always transitive!但这很少会导致堆栈溢出(除非Sort 方法使用某种 FP 方法);它可能会导致它永远运行,但我相信内置类型甚至已经涵盖了这一点(它们检查理论上的最大运行长度并中止,IIRC)。

    我会看AccountId;如果它做了一些“聪明”的事情(比如从父集合中延迟加载一些值),那么错误很可能在“聪明”中。

    【讨论】:

    • 传递性错误似乎已在 .Net 4.0 中修复
    【解决方案3】:

    所以我遇到了一个棘手的情况,我在比较方法中得到了 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)

    【讨论】:

    • 问题编辑是在我开始写我的帖子之后进行的。显然字符串不会有这样的问题。
    【解决方案4】:

    这是一个现成的想法:

    帐户是否声明List<Account>

    我想知道Accounts 是否是一个声明为List<Account> 以外的属性——例如,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
        }
        // ...
    }
    

    【讨论】:

      【解决方案5】:

      StackOverflowExceptions 通常发生在你有一个疯狂的递归调用时。检查SortAccountId 是否在给自己打电话。如果是这样,请检查这些递归函数的基本情况,并确保它们在应该停止时停止。

      【讨论】:

        【解决方案6】:

        仅仅因为这条线抛出 StackOverflow 并不意味着它是问题的原因,例如

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

        这与递归调用 methodA() 时一样可能在 b() 上发生堆栈溢出;

        我怀疑递归是在这行代码周围的方法上。

        【讨论】:

          猜你喜欢
          • 2020-12-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-05-26
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多