views:

324

answers:

5

I've been trying to write a simple Static-class State Machine for my application to notify other controls and code when the system state changes. And I think I almost have it, but I'm running into a small issue that I'm not sure how to work around.

Here's the code:

// 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;
    }
}

The problem I am having is with this line:

 OnStateChange(this, sysInfo);

Specifically, the word "this" is illegal. And I understand why: "this" is supposed to refer back to the self of an instantiated object (not a static class).

I would prefer to have a Static class for my state machine rather than one that I can instantiate multiple copies of. (Not that it would be such a bad thing, but I feel it makes the code cleaner having a static class.)

So how am I supposed to work this?

Update:

As a follow-up, I selected Jon Skeet's answer as the correct one because the issue was more about the approach I was taking, rather than a technical failure that I was having. Although, pretty much all of the other answers below fix the technical glitch I was dealing with.

Oddly enough, as I was reviewing with my co-worker the application that I wrote, she pointed out that the program should probably track both the state of the server connection as well as the state of the work being done. (Yes, Virginia, this means I need 2 state machines... Ergo, remove all the "static" keywords from the code above and make it a regular class was the smart approach.)

Thanks again, everyone!

+6  A: 

You can't use "this" when you're working within a static scope, such as a static class or a static method.

You have two options here. You can pass null for the "sys" parameter. Really, this parameter, in the case of a static class, is really not useful, since the "sender" is always the static class.

Alternatively, you might want to consider making your state notifier a singleton. This would allow you to have a single instance of a non-static class. This does have the one advantage of making it easier to transition to a non-static implementation if future requirements change, as well.


In addition, you really should check to make sure there are subscribers prior to raising this event. Not doing so could cause problems:

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);
    }
}
Reed Copsey
Good call on the subscriber check prior to raising the event. That's a big "duh". I should know better by now... :)
Pretzel
+13  A: 

Why would you want a static class? It's a state machine - it has state - that naturally suggests using a non-static class. You can always have a static variable referring to a single instance of it if you really want to.

Basically, your instinct is incorrect in my view - having a normal class would make the code cleaner than a static one. Static classes should very rarely have any state at all - perhaps a cache (although even that's dubious), or counters for diagnostic purposes etc. Try to think in terms of objects rather than classes. Does it make sense to have two separate state machines, with a different current state and perhaps different event handlers? It's easy to imagine that's the case - and it means it's easy to create new instances for tests etc. (It also allows independent tests to run in parallel.) Having the state in an instance of the machine is therefore a natural fit.

There are some people who believe there should be no static methods, no static classes etc. I think that's taking it a bit far, but you should always at least consider the testability impact of introducing statics.

Jon Skeet
This is a very valid point. I have a feeling, over time, a static class is going to problematic.
Reed Copsey
+1 best answer so far.
JonH
+1 Good point for design and testability.
Ron Klein
@JonH: It is on one hand, except that it doesn't actually answer the question in regards of how the OP would handle this properly if they want it to remain static. ;)
Reed Copsey
Hey Jon, thanks for questioning my instinct. I wasn't sure I was necessarily taking the right approach here (which is why I specifically brought it up.) You bring up a good point about potentially having a need for multiple state machines as well as testability. I think I'll redesign and forgo the Static class. Thanks!
Pretzel
Reed - I sort of agree but I think the explanation that Jon has given is enough to rethink this whole design out.
JonH
@JonH: I do too (which is why I voted this up ;) ) but it's an unusual "answer", since its addressing something outside the scope of the question.
Reed Copsey
@Reed: Questioning the assumptions behind the question is relatively common in my experience. Heck I've had another question along the same lines today: http://stackoverflow.com/questions/2407843
Jon Skeet
@Jon Skeet: btw, I just ordered your C# in Depth book a few days ago. I'm very much looking forward to it as I've heard good things about it and I badly need to get up to speed on C# 3.0
Pretzel
@Reed: I'm definitely appreciative that Jon went outside the scope of the question (and also that you answered my question so well) -- Thanks to everyone here, I now have many different approaches to try. It should be a good (and fun!) learning experience. :)
Pretzel
+1  A: 

Modify your delegate:

from:

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

to:

public static delegate void StateChangeHandler(SysInfoEventArgs sysStateInfo);
Yoopergeek
Despite years of programming under my belt, I can't help but recall a passage in a Charles Petzold book about Windows 3.x programming. (I never did learn how to write for Win3.x - ha!) Specifically, he was talking about how much C++ code goes into just creating a window and that at first you'll have to consider these first few hundred lines of code to be "incantations" and that later the reader would understand what is going on. Well, here I am, writing code and not even considering that I can change the passed parameters of my delegate... Thanks for the insight. I'll have to give it a shot.
Pretzel
+1  A: 

I'd change this code:

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

to:

public static event Action<SysInfoEventArgs> OnStateChange;
Ron Klein
Looks like I'm still stuck in C# 2.0 -- I just ordered Jon Skeet's "C# in Depth: What you need to master C# 2 and 3" a few days ago -- Hopefully it will get here soon... :) -- Thanks for the tip. I'll give it a shot.
Pretzel
+1  A: 

If you really want to keep the static class and use the semantics of object sender, then the proper thing to pass would be typeof(Sys). This is also analogous to the (old and rare) locking idiom on a static class.

But that's just being pedantic, because the event handler will never use that value, and in practice null would work just as well.

Jeffrey L Whitledge
Thanks Jeffrey. That's insightful, actually.
Pretzel