Простой конечный автомат, использующий статический класс в C # для уведомления других подписчиков через события - PullRequest
4 голосов
/ 09 марта 2010

Я пытался написать простой State-class State Machine для моего приложения, чтобы уведомлять другие элементы управления и код при изменении состояния системы. И я думаю, что почти у меня это есть, но я сталкиваюсь с небольшой проблемой, которую я не знаю, как обойти.

Вот код:

// An enum denoting the 3 States
public enum Status { Error = -1, Working, Ready }

// The main state change class
public static class Sys
{
    // system status
    private static Status state;

    // delegate and event
    public static delegate void StateChangeHandler(object sys, SysInfoEventArgs sysStateInfo);
    public static event StateChangeHandler OnStateChange;

    public static Status State
    {
        get { return state; }
        set
        {
            SysInfoEventArgs sysInfo = new SysInfoEventArgs(state, value);
            state = value;
            OnStateChange(this, sysInfo);
        }
    }
}

/// <summary>Contains previous and current state info</summary>
public class SysInfoEventArgs : EventArgs
{
    public readonly Status oldState;
    public readonly Status newState;
    public SysInfoEventArgs(Status oldState, Status newState)
    {
        this.oldState = oldState;
        this.newState = newState;
    }
}

У меня проблема с этой строкой:

 OnStateChange(this, sysInfo);

В частности, слово «это» является незаконным. И я понимаю, почему: «this» должно ссылаться на себя экземпляра объекта (не статического класса).

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

Так, как я должен работать это?

Обновление:

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

Как ни странно, когда я просматривал со своим коллегой приложение, которое я написал, она указала, что программа, вероятно, должна отслеживать как состояние соединения с сервером, так и состояние выполняемой работы. (Да, Вирджиния, это означает, что мне нужны 2 конечных автомата ... Ergo, удалите все «статические» ключевые слова из кода выше и сделайте его обычным классом.)

Еще раз спасибо всем!

Ответы [ 5 ]

14 голосов
/ 09 марта 2010

Зачем вам статический класс? Это машина state - она ​​имеет state - что, естественно, предполагает использование нестатического класса. У вас всегда может быть статическая переменная, ссылающаяся на один ее экземпляр, если вы действительно этого хотите.

По сути, ваш инстинкт неверен, на мой взгляд - наличие нормального класса сделает код чище, чем статический. Статические классы вообще очень редко должны иметь какое-либо состояние - возможно, кеш (хотя даже это сомнительно) или счетчики для диагностических целей и т. Д. Постарайтесь думать с точки зрения объектов , а не классов, Имеет ли смысл иметь два отдельных конечных автомата с другим текущим состоянием и, возможно, разными обработчиками событий? Легко представить, что это так - и это означает, что легко создавать новые экземпляры для тестов и т. Д. (Это также позволяет выполнять независимые тесты параллельно.) Поэтому наличие состояния в экземпляре машины является естественным соответствием.

Есть некоторые люди, которые считают, что должны быть нет статические методы, нет статические классы и т. Д. Я думаю, что это немного далеко, но вы всегда должны по крайней мере рассмотрим влияние тестируемости на введение статики.

6 голосов
/ 09 марта 2010

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

У вас есть два варианта здесь. Вы можете передать null для параметра "sys". Действительно, в случае статического класса этот параметр действительно бесполезен, поскольку «отправитель» всегда является статическим классом.

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


Кроме того, вы действительно должны проверить, есть ли подписчики до начала этого события. Невыполнение этого требования может вызвать проблемы:

public static Status State
{
    get { return state; }
    set
    {
        SysInfoEventArgs sysInfo = new SysInfoEventArgs(state, value);
        state = value;
        var handler = OnStateChange;
        if (handler != null)
            handler(null, sysInfo);
    }
}
1 голос
/ 09 марта 2010

Если вы действительно хотите сохранить статический класс и использовать семантику object sender, тогда правильной передачей будет typeof(Sys). Это также аналогично (старой и редкой) блокирующей идиоме для статического класса.

Но это просто педантично, потому что обработчик событий никогда не будет использовать это значение, и на практике null будет работать так же хорошо.

1 голос
/ 09 марта 2010

Я бы изменил этот код:

public static delegate void StateChangeHandler(object sys, SysInfoEventArgs sysStateInfo);
public static event StateChangeHandler OnStateChange;

до:

public static event Action<SysInfoEventArgs> OnStateChange;
1 голос
/ 09 марта 2010

Изменить ваш делегат:

от

public static delegate void StateChangeHandler(object sys, SysInfoEventArgs sysStateInfo);

до:

public static delegate void StateChangeHandler(SysInfoEventArgs sysStateInfo);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...