Статический элемент не сохраняет назначенное значение

Хорошо, у меня есть статический класс с двумя статическими членами, строковым и логическим.
Общедоступный статический метод присваивает значения этим членам на основе состояния переданных параметров.

Затем вызывается закрытый статический метод, который обрабатывает статические члены.

Проблема в том, что логическое значение сохраняет значение, установленное в публичной функции, а строка — нет; по умолчанию он возвращается к своему инициализированному значению. Почему это так?
Ниже приведен упрощенный код.

static class MessageHandler
{
    private static String m_messageToSend = String.Empty;
    private static bool m_requiresACK = false;


    public static void Send(String message)
    {
         //formatting etc (actual method sets more fields)
         m_messageToSend = message;
         m_requiresACK = true;

         Send();
    }

    private void static Send()
    {
        SendMessageDelegate sendDelegate = DoSend;
        //At this point m_requiresACK remains true but m_messageToSend does not 
        //hold value of message; it is empty.
        IAsyncResult ar = sendDelegate.BeginInvoke(m_messageToSend, m_requiresACK);


        //rest of function
    }
}

//some other class
MessageHandler.Send("Hello");

person Kildareflare    schedule 17.07.2009    source источник
comment
Вы уверены, что он правильно назначается в методе Send(string). Вы ничего не забыли упомянуть при упрощении кода?   -  person Samuel Carrijo    schedule 17.07.2009
comment
Почему это статический класс вообще?   -  person devio    schedule 17.07.2009
comment
Девио - может и не должно быть, я пока только учусь. Это казалось самым элегантным решением и не требовало создания объекта. Имя класса не очень хорошее и нуждается в изменении - возможно, MessageHandler, так как он пересылает их дальше. MessageHandler.Отправить(Сообщение).   -  person Kildareflare    schedule 17.07.2009


Ответы (5)


"Небезопасность" потока этого кода может быть проблемой, поскольку другие потоки могут вызывать Send(string), пока ваш поток находится в середине того же метода. Я бы предложил следующую переработку класса Message:

static class Message
{
    public static void Send(String message)
    {
         Send(message, true);
    }

    private void static Send(string messageToSend, bool requiresACK)
    {
        SendMessageDelegate sendDelegate = DoSend;
        IAsyncResult ar = sendDelegate.BeginInvoke(messageToSend, requiresACK);

        //rest of function
    }
}
person Peter Lillevold    schedule 17.07.2009
comment
Питер, кажется, это решило проблему. Так получилось, что я использовал члены напрямую, так как недавно склонился к этому, вместо того, чтобы передавать множество параметров (на самом деле их больше, чем указано). Однако, конечно, в этом примере было бы разумнее передать параметры - лошади для курсов. - person Kildareflare; 17.07.2009
comment
С переменными-членами обычно все в порядке, но статические переменные-члены требуют особого внимания, когда речь идет о безопасности потоков. Рад, что устранил проблему. - person Peter Lillevold; 17.07.2009

Скорее всего, это связано с тем, что другой поток вызывает

Message.Send("");

или ваш AppDomain выгружается. Без дополнительной информации трудно сказать наверняка.

person Andrew Hare    schedule 17.07.2009

У вас есть огромные проблемы с безопасностью потоков. Если вам действительно нужна эта статика, есть дерзкое исправление:

[ThreadStatic]
private static String m_messageToSend = String.Empty;
[ThreadStatic]
private static bool m_requiresACK = false;

Теперь он действует как статический, но для каждого потока. Кризис предотвращен; но это немного... ну, я бы сам постарался избежать необходимости - но это будет работать нормально.

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

person Marc Gravell    schedule 17.07.2009
comment
Это будет вызываться только в одном потоке за раз, однако спасибо за совет! У меня был метод инициализации, но я удалил его, чтобы уменьшить код. - person Kildareflare; 17.07.2009

Я был бы очень удивлен, если бы одно поле сохраняло одно и то же значение, а другое — нет.

Если это веб-приложение, это может произойти, если приложение переработано.

person Philippe Leybaert    schedule 17.07.2009

Я вижу в ваших комментариях, что после этой первой строки частная переменная пуста, а не содержит значение параметра. Верно ли это, как только вы нажмете первую строку Send()?

Напишите модульный тест или простую тестовую обвязку, которая вызывает Message.Send("Hello World"); и делает утверждение на выходе. Изоляция этого от вашей вызывающей кодовой базы может пролить свет на то, является ли это вашим классом Message, который ведет себя ненормально, или это потребитель, отправляющий неверные/неожиданные данные.

Кроме того, если ваша //остальная функция не включает сброс вашего логического значения, она всегда будет истинной после отправки первого сообщения.

person Andy_Vulhop    schedule 17.07.2009
comment
Как это бывает, да, bool сбрасывается - там тоже много другого форматирования и логики. Я сильно упростил код, чтобы он сосредоточился только на проблеме, с которой у меня были проблемы. Я думаю, что в будущем я добавлю больше комментариев, чтобы подчеркнуть это ... хотя это хорошо замечено. - person Kildareflare; 17.07.2009
comment
Понял и вроде как ожидал. Я просто добавил, что это случайно, что он не сбрасывался. Время от времени низко висящие плоды действительно срабатывают. - person Andy_Vulhop; 17.07.2009