Структуры и переполнение стека C # - PullRequest
1 голос
/ 01 апреля 2011

Привет, у меня есть эта программа, которую я пишу, которая использует структуру ниже. Я продолжаю получать сообщение об ошибке переполнения стека, и исключение заключается в том, что программа останавливается на первой скобке в наборе открытых строк sessionID ... (где ">>>").

  public struct SessionStruct 
{
    public string sessionID
    {
        get
        {
            return sessionID;
        }
        set
  >>>   {
            sessionID = value;
        }
    } 
    public DateTime time
    {
        get
        {
            return time;
        }
        set
        {
            time = value;
        }
    }

    public string type
    {
        get
        {
            return type;
        }
        set
        {
            type = value;
        }
    } 
};

Вот код, который устанавливает структуру:

if (type == "11" || type == "9")
                {
                    s.sessionID = attributeArray[0].ToString();
                    s.time = DateTime.Now;
                    if (type == "9")
                        s.type = attributeArray[4].ToString();
                }
                else
                {
                    s.sessionID = null;
                    s.time = DateTime.Now;
                    s.type = null;
                }

Заранее спасибо за помощь ...

Ответы [ 8 ]

7 голосов
/ 01 апреля 2011

вы делаете бесконечную рекурсию в этом сеттере.Думаю об этом.Создайте приватный член с другим именем для получения / установки.

private string sessionID;
public string SessionID
    {
        get
        {
            return sessionID;
        }
        set
        {
            sessionID = value;
        }
    }
5 голосов
/ 01 апреля 2011

Установщик свойства sessionId вызывает его сам, вызывая вечную рекурсию.

4 голосов
/ 01 апреля 2011

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

private string _sessionID;

public string sessionID
{
    get
    {
        return _sessionID;
    }
    set
    {
        _sessionID = value;
    }
} 

Или пусть компилятор сделает это за вас:

public string sessionID
{
    // compiler will create a backing store for you
    get; set; 
} 
3 голосов
/ 01 апреля 2011

Это потому, что свойство set вызывает само себя (имя то же самое).

Возможно, вы хотите использовать autoproperty

public string sessionID
{
    get; set;
} 
1 голос
/ 01 апреля 2011

Как все уже сказали, вы делаете бесконечную рекурсию. В отличие от всех остальных, я собираюсь объяснить вам, почему бесконечная рекурсия вызывает переполнение стека:

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

Когда какой-либо код пытается установить свойство, за сценой вызывается функция установки, передавая значение в качестве аргумента. Внутренне ваш s.sessionID = attributeArray[0].ToString(); по сути s.set_sessionID(attributeArray[0].ToString());. Так что ваш сеттер вызывается. Но сеттер также пытается установить свойство. На самом деле, sessionID = value; - это просто более чистый способ сказать set_sessionID(value);. Итак, ваш установщик снова вызывает его. Этот вызов попытается выполнить оператор sessionID = value;, который вызывает установщик, который пытается выполнить оператор, который вызывает установщик, что ... хорошо, я надеюсь, вы поняли идею.

Функция, которая вызывает себя, называется рекурсивной , а саму технику обычно называют рекурсия . Есть несколько хороших применений для рекурсии, но обычно они используют некоторую форму условного ветвления (поэтому, в конце концов, условие не выполняется, и функция перестает вызывать себя и начинает давать результаты). Однако в вашем случае вы идете к бесконечности и дальше: в вашем коде нет ничего, что могло бы остановить рекурсию (главным образом потому, что она не была предназначена для рекурсии в конце концов), поэтому она будет продолжаться бесконечно. Ну, не бесконечно, просто до тех пор, пока не выйдет ваша программа: P

Теперь, в сочной части этого ответа: какого черта это StackOverflow? (кроме хорошего сайта Q / A). Почему вы не получаете что-то вроде InfiniteRecursionException? Конечно, среда выполнения не знает, испортили ли вы или просто делаете что-то необычное (на самом деле, среда выполнения надеется , что вы делаете что-то вменяемое), поэтому она доверяет вам и продолжает делать то, что ваш код приказывает это сделать (в этом случае вызовите установщик свойств).

Но чтобы вызов функции работал, среда выполнения должна вставить «указатель» на вызывающий набор состояний и аргументы вызова в стек. Указатель необходим для того, чтобы среда выполнения могла выяснить, куда идти после возврата вызванной функции. И аргументы необходимы, чтобы функция знала, где их искать (в конце концов, она не знает, откуда она будет вызываться). При каждом вызове эти вещи помещаются на вершину стека, поверх всего, что уже было там.

Обычно функция выдает (извлекает и удаляет) аргументы, делает некоторые вещи и возвращает. В этот момент (аргументов больше нет), «указатель возврата» находится на вершине стека, поэтому среда выполнения выдает его и использует для возврата управления вызывающей стороне.

Именно из-за этого механизма суммирования (все помещаемое в очередь идет поверх всего остального, а вещи извлекаются только из верхней части стека), что цепные вызовы могут работать (например: функция A вызывает функцию B, которая вызывает функцию C : когда C вернется, управление вернется к B, а не к A, но как только сам B вернется, управление вернется к A).

Что произойдет, если вы продолжите лить воду на стакан? Обычно он заполнится (если у него нет отверстия или что-то). Но что, если вы продолжите поливать его водой, когда она уже заполнена? Вода вытекает из стекла, потому что она не может поместиться. Мы могли бы сказать, что вода перетекает из стекла.

Теперь давайте снова посмотрим на вашу бесконечную рекурсию: каждый раз, когда вы устанавливаете свойство, вы делаете (неявно) вызов метода установки, поэтому выдвигаются новый «указатель возврата» и аргумент (значение)в стек.Поскольку вызов выполняет новый вызов до того, как он сможет вернуться, он бесконечно помещает указатели возврата в стек до того, как они получат шанс получить ответ.Таким образом, стек заполняется до тех пор, пока не останется свободной памяти.В этот момент стек переполняется.И, поскольку у программы нет абсолютно никакого способа продолжить работу без помещения чего-либо в стек (чего среда выполнения не может сделать, так как для этого больше нет места), единственная оставшаяся возможность - жаловаться на проблему.Эта жалоба во время выполнения - это то, что обычно называют StackOverflowException.


BTW, вы можете видеть (упрощенный образец) стека вызовов при отладке вашей программы.Запустите его через отладчик Visual Studio (по умолчанию вы можете сделать это, нажав клавишу F5) и дождитесь его сбоя с исключением SO.Затем найдите окно «Call Stack» (я не могу вспомнить, где оно появляется по умолчанию, перемещало его бесчисленное количество раз), и вы увидите, насколько глубоко среда выполнения смогла пройти рекурсию, прежде чем закончится пространство стека.,Ваш сеттер должен показываться там строка за строкой, бесчисленное количество раз.


И, наконец, упоминание о решениях: все, что вам нужно сделать, это избежать рекурсии.Это означает, что вы не можете вызывать свой установщик изнутри вашего установщика (устанавливая то же свойство из установщика).Это также относится к методам получения (которые вызываются при получении).

Типичный способ сделать это - определить поле «backing» для свойства (переменная-член, где значение будет фактически сохранено), изатем из вашего установщика и получателя вы устанавливаете или возвращаете это поле соответственно.Поскольку поле должно иметь другое имя (типичный подход - использовать подчеркивание (_), за которым следует имя свойства), компилятор будет знать, что вы задаете поле, а не свойство, поэтому выиграл 'не пытайтесь рекурсивно вызвать установщик вашей собственности.Обычно сеттеры полезны для выполнения «проверок работоспособности» (например, вы можете проверить, что переданное значение не равно null), но существует много ситуаций, когда нет необходимости проверять ни сеттер, ни геттер, инеобходимость определения вспомогательных полей и самих установщиков / получателей становится скучной, раздутой, избыточной и подверженной ошибкам.По этой причине C # поддерживает «автоматически реализуемые» свойства: если вы замените оба средства доступа на get; set; (а свойство не является абстрактным), компилятор добавит «безымянное» вспомогательное поле и получит/ установка кода для вас.Обратите внимание, что для того, чтобы это работало, вы должны оставить оба средства доступа: если вы хотите определить один из них, вам также нужно будет определить другой.

Если вы уже поняли концепции, посмотрите надругие ответы, чтобы увидеть много примеров того, как их применять;)

Надеюсь, это поможет.

1 голос
/ 01 апреля 2011

Проблема в том, что вы возвращаетесь к свойству sessionID.

Это:

public string sessionID
{
    get
    {
        return sessionID;
    }
    set
    {
        sessionID = value;
    }
}

Будет скомпилировано примерно так:

public string sessionID_get()           { return sessionID_get(); }
public void sessionID_set(string value) { sessionID_set(value);   }

Очевидно, что это не сработает!

Вы должны использовать вспомогательное поле:

private string _sessionID;
public string sessionID
{
    get
    {
        return _sessionID;
    }
    set
    {
        _sessionID = value;
    }
}

В качестве альтернативы, вы можете заставить компилятор автоматически сгенерировать его:

public string sessionID
{
    get; set;
}
0 голосов
/ 01 апреля 2011

Вы назначаете sessionId для себя в вашем сетевом аксессоре. (Рекурсивный)

Вам нужно разделить поле, которое вы вводите, на собственное поле поддержки или использовать автоматическое свойство.

Вот так:

  private string _sessionId;

    public string sessionID
    {
        get
        {
            return _sessionId;
        }
        set
        {
            _sessionId= value;
        }
    } 

или автоматическое свойство:

public string sessionId { get;set ;}

Также обратите внимание : у других свойств возникнет та же проблема при доступе к набору доступа.

0 голосов
/ 01 апреля 2011

Вы должны использовать поле поддержки, если вы реализуете getter и setter самостоятельно, иначе оно будет продолжать вызывать себя на setter, например, так:

private string _sessionId;
public string sessionID
{
    get
    {
        return _sessionID;
    }
    set
    {
        _sessionID = value;
    }

}

Так как вына самом деле вам не нужны реализации, поскольку вы не добавили никакой пользовательской логики, используйте автоматические свойства:

public string sessionID {get;set;}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...