StackOverflowException без рекурсии или бесконечного цикла? - PullRequest
8 голосов
/ 23 апреля 2009

Фон

У меня есть элемент управления DataGridView, который я использую, и я добавил свой обработчик ниже к событию DataGridView.CellFormatting, чтобы значения в некоторых ячейках можно было сделать более понятными для человека. Этот обработчик событий работал отлично, форматируя все значения без проблем.

Однако недавно я обнаружил, что очень редкое обстоятельство вызывает необычную ошибку. Столбец в моем DataGridView для даты выполнения элемента всегда имеет значение int. 0 указывает, что событие никогда не наступает, любое другое значение является отметкой времени UTC для даты исполнения. Соответствующий столбец базы данных MySQL не допускает нулевые значения. Когда пользователь переместился из одной строки DataGridView с датой исполнения, в другую строку DataGridView с датой исполнения (в этот момент все по-прежнему выглядит нормально), а затем нажимает кнопку, которая перезагружает данные из базы данных без отправки обновлений, по сути вызова DataAdapter.Fill()), программа генерирует StackOverflowException**.

Нет рекурсии?

Что для меня так необычно, так это то, что я не вижу, где находится рекурсия или бесконечный цикл. Я добавил int cellFormatCallCount в качестве члена класса и увеличивал его во время каждого вызова, но в момент выдачи исключения отладчик показывает значение этого int как 1, что я ожидаю, так как не находился под происходило впечатление и рекурсия.

Может кто-нибудь мне помочь?

Как посмотреть трассировку стека? В VS2008 это говорит: {Cannot evaluate expression because the current thread is in a stack overflow state.}

С уважением,

Robinson

private int cellFormatCallCount = 0;
private void myDataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)  {
    try {
        // to format the cell, we will need to know its value and which column its in
        string value = "";
        string column = "";

        // the event is sometimes called where the value property is null
        if (e.Value != null) {
            cellFormatCallCount++; // here is my test for recursion, this class member will increment each call

            // This is the line that throws the StackOverflowException
            /* ********************* */
            value = e.Value.ToString();
            /* ********************* */

            column = actionsDataGridView.Columns[e.ColumnIndex].Name;
        } else {
            return; // null values cannont be formatted, so return
        }

        if (column == "id") {
            // different code for formatting each column
        } else if (column == "title") {
            // ...
        } else {
            // ...
        }
    } finally {
        cellFormatCallCount = 0; // after we are done with the formatting, reset our recursion counter
    }
}

Ответы [ 7 ]

5 голосов
/ 23 апреля 2009

Видимо, e.Value.ToString () снова вызывает событие CellFormatting. Это кажется несколько логичным. Это должно быть достаточно легко выяснить с помощью отладчика.

Но реальная рекурсия может быть вызвана где-то еще, например, в форматировании по столбцам, которое вы пропустили.

Ваша рекурсивная проверка не является надежной, поскольку Value == null также сбрасывает ее, и она, по-видимому, используется всеми столбцами. Заставьте его окружать e.Value.ToString () более плотно:

if (e.Value != null) 
{
   cellFormatCallCount++; 
   System.Diagnostics.Debug.Assert(cellFormatCallCount <= 1, "Recursion");
   value = e.Value.ToString();
   cellFormatCallCount--; 
   ...
} 
2 голосов
/ 23 апреля 2009

Полностью случайное предположение (без трассировки стека это все, что я могу сделать) ...

Вы пытаетесь отобразить / отформатировать тип с потенциально рекурсивным ToString()?

public string ToString()
{
   return ... this.ToString() ...
   // or
   return String.Format("The value is {0}", this);
}

Опечатка / такая ошибка может привести к StackOverflowException ...

1 голос
/ 23 апреля 2009

Попробуйте сделать статическую переменную cellFormatCallCount, чтобы она использовалась всеми экземплярами класса. Я подозреваю, что событие как-то срабатывает само по себе, но вы его не видите, потому что cellFormatCallCount является локальным для каждого экземпляра класса, обрабатывающего событие, и, следовательно, никогда не увеличивается на единицу больше 1. Если это так, то фактический триггер для стекового потока (рекурсия) может быть в любом месте метода, и в этой строке просто не хватает места в стеке.

После того, как вы сделали переменную статической, вы можете выдать исключение, когда оно превышает определенное (маленькое) значение, например 2. Это исключение должно оставить видимую трассировку стека.

1 голос
/ 23 апреля 2009

Учитывая, что это событие, может ли оно вызывать себя?

1 голос
/ 23 апреля 2009

@ Даниэль: Если бы это было проблемой, разве это не подняло бы исключение в строке:

if (e.Value != null) {

@ gnirts: Не могли бы вы опубликовать полный метод и трассировку стека?

@ BCS (ниже): Я думаю, что это может быть, но это может быть легко в некотором коде, который не показан в опубликованном демо.

PS. Извините, это должен был быть комментарий, но у меня недостаточно повторений: -D

0 голосов
/ 11 января 2012

У меня только что была похожая проблема со стеком потока без подробностей трассировки.

Моя проблема была связана с экземпляром объекта, который не должен был создаваться. Я удалил оскорбительный новый объект, и все было в порядке.

В вышеуказанных обстоятельствах, не видя больше кода, убедитесь, что несколько событий не запущены, используя = - для соответствующей отмены событий.

Надеюсь, это кому-нибудь поможет.

0 голосов
/ 23 апреля 2009

Это не связано с проблемой, но на самом деле вы можете иметь StackOverflowException без рекурсии вообще только с:

throw new StackOverflowException();
...