ILoggable

A place to keep my thoughts on programming

 Subscribe

geekblog
[at]
claassen [dot] net

Powered by Blogger

Sunday, February 11, 2007

Discoverable RangeValidation (revisited)

The RangeValidation code I created turned out to be of limited usefulness because Attributes are pretty picky about what they can take as arguments. This meant that I couldn't even create the decimal version, which was the original motivator. Revisiting the design I refactored the range validation code into a generic Attribute paired with a Helper class using generics to be type-safe. This also changed the usage to the following:

/// <summary>
/// Valid from 0 to 10
/// </summary>
[ValidateRange(0, 10)]
public decimal Rating
{
  get { return rating; }
  set
  {
    ValidateRange<decimal>.Validate(value);
    rating = value;
  }
}

/// <summary>
/// Valid from 0 to Int.MaxValue
/// </summary>
[ValidateRange(Min = 0)]
public int Positive
{
  get { return positive; }
  set
  {
    ValidateRange<int>.Validate(value);
    positive = value;
  }
}

The ValidateRange class now simply takes the values and stores them as objects, leaving interpretation to the generic class ValidateRange<T>.

[AttributeUsage(AttributeTargets.Property)]
public class ValidateRange : Attribute
{
  object min = null;
  object max = null;

  /// <summary>
  /// Validation with both an upper and lower bound
  /// </summary>
  /// <param name="min"></param>
  /// <param name="max"></param>
  public ValidateRange(object min, object max)
  {
    this.min = min;
    this.max = max;
  }

  /// <summary>
  /// Validation with only upper or lower bound.
  /// Must also specify named parameter Min or Max
  /// </summary>
  public ValidateRange()
  {
  }

  public object Min
  {
    get { return min; }
    set { min = value; }
  }

  public object Max
  {
    get { return max; }
    set { max = value; }
  }
}

The generic class ValidateRange<T> still has the static Validate accessor which has to be used in the property that is tagged with the ValidateRange attribute, since it divines the property from the StackTrace. But given a PropertyInfo instance, the class can also be instantiated independently, so that Validation code can inspect the range requirements and communicate them to the enduser, rather than only reacting to the exceptions thrown by the Property in question.

public class ValidateRange<T> where T : IComparable
{
  /// <summary>
  /// Must be used from within the set part of the Property.
  /// It divines the Caller to perform validation.
  /// </summary>
  /// <param name="value"></param>
  static public void Validate(T value)
  {
    StackTrace trace = new StackTrace();
    StackFrame frame = trace.GetFrame(1);
    MethodBase methodBase = frame.GetMethod();
    // there has to be a better way to get PropertyInfo from methodBase
    PropertyInfo property
      = methodBase.DeclaringType.GetProperty(methodBase.Name.Substring(4));
    ValidateRange<T> validator = new ValidateRange<T>(property);
    validator.CheckRange(value);
  }

  bool hasMin = false;
  bool hasMax = false;
  T min;
  T max;
  PropertyInfo property;

  public ValidateRange(PropertyInfo property)
  {
    this.property = property;
    ValidateRange validationAttribute = null;
    try
    {
      // we make the assumption that if the caller is using
      // this object, they defined the attribute on the passed property
      validationAttribute
        = (ValidateRange)property.GetCustomAttributes(typeof(ValidateRange), false)[0];
    }
    catch (Exception e)
    {
      throw new InvalidOperationException(
        "ValidateRange Attribute not defined", e);
    }
    if (validationAttribute.Min != null)
    {
      hasMin = true;
      min = (T)Convert.ChangeType(validationAttribute.Min, min.GetType());
    }
    if (validationAttribute.Max != null)
    {
      hasMax = true;
      max = (T)Convert.ChangeType(validationAttribute.Max, max.GetType());
    }
  }

  public bool HasMin
  {
    get { return hasMin; }
  }

  public bool HasMax
  {
    get { return hasMax; }
  }

  public T Min
  {
    get { return min; }
  }

  public T Max
  {
    get { return max; }
  }

  private void CheckRange(T value)
  {
    if (HasMax && value.CompareTo(max) == 1)
    {
      throw new ArgumentOutOfRangeException(
        property.Name,
        "Value cannot be greater than " + max);
    }
    else if (HasMin && value.CompareTo(min) == -1)
    {
      throw new ArgumentOutOfRangeException(
        property.Name,
        "Value cannot be less than " + min);
    }
  }
}

Saturday, February 10, 2007

Discoverable RangeValidation

One aspect of writing code that has always bothered me is how to communicate legal values. Sure, if the type that a Property accepts is of your own making then the onus is on the one who created the instance to make sure it's a legal value. But so many Properties are value types and not every value is valid. Now, you say that's why we have validators, it's their job to check the validity and tell the user what they did wrong. Fine. That's the UI element for communicating the problem. But what always seemed wrong was that in your UI code you specified what values were legal. And if the use of business level logic in UI code wasn't bad enough, then how about the question of where the valid values came from anyway? Did the programmer inately know what was valid or did the values come from the documentation? Neither allows programatic discovery.

Attributes for discoverability

Now, maybe i'm late to the party and this has been obvious to everyone else, but at least in my exposure to .NET programming Attributes have only cropped up occasionally--when the platform provided or required them (primarily Xml Serialization). But I'm quickly coming around to creating my own. They're an easy way to attach all sorts of meta data that would usually be buried in a documentation paragraph.

Anyway, the other day I was creating an object model that included a number of numeric properties with limited legal ranges. So I a) documented this range limitation and b)created a helper class that would range check my value and throw ArgumentOutOfRangeException as needed. Standard stuff. Then I exposed the class via a PropertyGrid which got me add Design Time support via attributes.

If you haven't ever created a User Control or Custom Control for others to use, you may not be familiar with these attributes. Once you drag your own Control into a form all your public properties become visible as properties in the Property window under Misc. This isn't always desirable or at least not self-explanatory. To help make your controls easier to use, you can use the [Browsable(false)] to mark your property as inaccessible for design time support. If you do want to use them at design time, Description and Category are two important attributes for making your Control more accessible, providing a description and a proper Category label for your property, respectively. In general, there are a lot of useful Attributes in System.ComponentModel for Design Time support.

Adding these Attributes, I decided that the same mechanism could be used to communicate valid values for any concerned party to divine. It looks like this had already been considered by MS, but only as part of Powershell, with the ValidateRange Attribute. So I wrote my own interpretation, complete with static helper to perform validation. This last part makes for maintainable code, but i'm not sure of performance overhead introduced, so use with caution.

The implementation is type specific and shown here as an Int range Validator. Unfortunately classes deriving from Attribute can't be Generic. Another nuisance is that Nullable types are not valid as parameters for Attributes.

namespace Droog.Validation
{
  [AttributeUsage(AttributeTargets.Property)]
  public class ValidateIntRange : Attribute
  {
    /// <summary>
    /// Must be used from within the set part of the Property.
    /// It divines the Caller to perform validation.
    /// </summary>
    /// <param name="value"></param>
    static public void Validate(int value)
    {
      StackTrace trace = new StackTrace();
      StackFrame frame = trace.GetFrame(1);
      MethodBase methodBase = frame.GetMethod();
      // there has to be a better way to get PropertyInfo from methodBase
      PropertyInfo property 
        = methodBase.DeclaringType.GetProperty(methodBase.Name.Substring(4));
      ValidateIntRange rangeValidator = null;
      try
      {
        // we make the assumption that if the caller is using
        // this method, they defined the attribute
        rangeValidator 
          = (ValidateIntRange)property.GetCustomAttributes(typeof(ValidateIntRange), false)[0];
      }
      catch (Exception e)
      {
        throw new InvalidOperationException(
          "Cannot call Validate if the ValidateIntRange Attribute is not defined", e);
      }
      rangeValidator.Validate(value, property);
    }

    int? min = null;
    int? max = null;

    /// <summary>
    /// Validation with both an upper and lower bound
    /// </summary>
    /// <param name="min"></param>
    /// <param name="max"></param>
    public ValidateIntRange(int min, int max)
    {
      this.min = min;
      this.max = max;
    }

    /// <summary>
    /// Validation with only upper or lower bound.
    /// Must also specify named parameter Min or Max
    /// </summary>
    public ValidateIntRange()
    {
    }

    public bool HasMin
    {
      get { return (min == null) ? false : true; }
    }

    public bool HasMax
    {
      get { return (max == null) ? false : true; }
    }

    public int Min
    {
      get { return (int)min; }
      set { min = value; }
    }

    public int Max
    {
      get { return (int)max; }
      set { max = value; }
    }

    private void Validate(int value, PropertyInfo property)
    {
      if (max != null && value > max)
      {
        throw new ArgumentOutOfRangeException(
          property.Name,
          "Value cannot be greater than " + max);
      }
      else if (min != null && value < min)
      {
        throw new ArgumentOutOfRangeException(
          property.Name,
          "Value cannot be less than " + min);
      }
    }
  }
}

To properly take advantage of this Attribute, we must both attach the attribute to a property and call the Validation method:

/// <summary>
/// Valid from 0 to 10
/// </summary>
[ValidateIntRange(0, 10)]
public int Rating
{
  get { return rating; }
  set
  {
    ValidateIntRange.Validate(value);
    rating = value;
  }
}

/// <summary>
/// Valid from 0 to Int.MaxValue
/// </summary>
[ValidateIntRange(Min = 0)]
public int Positive
{
  get { return positive; }
  set
  {
    ValidateIntRange.Validate(value);
    positive = value;
  }
}

Voila, Properties with range validation and anyone using this class can programatically determine the valid range and use that information in their UI, etc.

Update:

Turns out I was a bit quick on the trigger. Attributes are even more limited than I thought in the type of data that they accept. The moment i tried to create decimal version of the above, I got stuck. So what I will probably do is create ValidateRange which takes strings and then uses Reflection on the caller to determine what type the values should be cast to. It'll be more flexible as well, as I'll need only one ValidateRange instead of one per numeric type.

Tuesday, February 06, 2007

WM5 Multiline Textbox draw bug Hack

I know I'm not the only .NET developer for Smartphone. But it sure seemed that way when I tried to find a solution for a .NETCF 1.0 bug with multiline textboxes, hardly an uncommon Control, IMHO. After trying a number of different hacks, I finally have constructed one that seems to solve all problems. But what is this problem first of all?

The Bug: Multiline Textbox will not properly paint its background on Windows Media 5 devices under .NET Compact Framework 1.0

When the textbox is drawn, it will paint the back on any area that contains text, but leave any empty areas unpainted, i.e. showing whatever was previously on the screen. This is aggravated by the fact that T9 textinput will often partially obscure the Textbox and once the T9 pop-up disappears the Textbox won't repaint the dirty areas. The closest thing to an official recognition of this bug that I could find was a post on the MSDN Forums that said it was known and to switch to .NETCF 2.0. Now, I would love to switch to 2.0, since Windows.Forms on 1.0 of .NETCF is missing a lot. Unfortunately all current Smartphones ship with 1.0. I could require users to upgrade their phones. But even if i just plug the ARM .cab out of MS's redistributable, that's still a 6MB install, not insignficant for loading over the phone network nor for the memory available on the device. And all that so they can run a 500KB app? Right. That path is not an option.

The Solution: "Wipe" the textbox with an overlay on refresh

I initially hacked around the bug by first drawing the background and using a timer to draw the textbox itself a moment later. While this worked initially, it added a complication in having to hide the textbox to refresh it. Hiding it made it loose focus, so I had to catch that and refocus it. This in turn does screwy things with T9 text entry modes on most phones I tested.

The solution that does work without any sideeffects that i've found is simply to overlay the textbox with a background colored panel for a moment. Once the panel is removed, the Textbox repaints itself and any area not repainted because of the bug is left with the color of the overlay/. This does not affect focus, so it can be done at any time.

Let's assume you have TextBox textBox and a Panel wiper of equal size and placed overlapping in the form, as well as a disabled Timer timer running at the shortest allowable interval. Finally you have a bool wipe to indicate whether we've performed the wipe. With this setup the logic for "wiping" the TextBox clean is as follows:

private void Wipe()
{
  wiper.Visible = true;
  wiper.BringToFront();
  timer.Enabled = true;
  wipe = true;
}

private void timer_Tick(object sender, EventArgs e)
{
  if (wipe)
  {
    wiper.Visible = false;
    timer.Enabled = false;
  }
}

That solves the problem of painting the areas the TextBox forgets. We can call Wipe() on Refresh() or pretty much any time we know we obscured the control. Wouldn't it be useful if Invalidate was virtual in 1.0 as well? ho hum. Unfortunately it's completely up to you to know when your TextBox is Invalidated and needs a wipe-down.

If you are handling the UI, that's manageable, but what about those T9 pop-ups? For all your program knows there is no such thing. Even if you derived a class off of TextBox and overrode its OnPaint(), you'd still never see a paint message, because the actual textbox painting caused by the T9 pop-up appears to happen at the OS level, below .NET. To detect a T9 pop-up, or let's say, to infer that it's happening, you have to watch both the KeyDown and KeyPress events. As soon a s the T9 pop-up happens you won't be seeing your characters come through as KeyPresses anymore. Instead you will see KeyDown events being fired with a KeyCode of ProcessKey. The next time you see a KeyPress event, it's all the keys of the completed word being spewn into the TextBox. Therefore, if you see a keypress come in after a ProcessKey, you know a T9 pop-up just closed and it's a good time to wipe down your code>TextBox. Given a flag bool inT9, the code would look look this:

void textBox_KeyPress(object sender, KeyPressEventArgs e)
{
  if (inT9)
  {
    Wipe();
    inT9 = false;
  }
}

void textBox_KeyDown(object sender, KeyEventArgs e)
{
  if (e.KeyCode == Keys.ProcessKey)
  {
    inT9 = true;
  }
}

At the end of the day this is all fairly elaborate just to use a standard multi-line code>TextBox, but I have not found another way to do it on a stock Smartphone under .NETCF. I'd be glad to be proven wrong, but in the meantime, at least this is an option.

Labels: , , , ,

Smartphone, wake up behavior

I've been doing quite a bit of work with the HTC Dash Smartphone. One thing all smartphones (and all phones really) is to go to sleep quickly to save battery. Well, on the Dash, when you push any button, it just wakes up and doesn't act on the button. Seems like the proper behavior, imho.

Then i switched over to the Nokia 2125 and it would wake up and then take the action appropriate for the pressed button. Really? I mean, don't execute an action that I can't even know the effects on until the screen is on and i can see the context. Finding the behavior annoying, I blamed Nokia for creating a bad implementation.

That got me curious, so I took a survey of all smartphones available to me and the 3125, SDA and xv6700 (PocketPC, not Smartphone, i know) all do this. Only the Blackjack behaves like the Dash, and at least in my mind, properly.

Labels: ,

Friday, February 02, 2007

State-aware programming in C#, part III

Now that we have a syntax for defining state aware objects, we need the code that wires these things up for us automatically. This consists of the following:

The StateMethod Attribute

This attribute simply marks the method as a state aware method. As previously illustrated, there is a lot of manual footwork to get all the plumbing in place for a method marked as such. I'm still trying to figure out whether I can simplify that some more and let the baseclass do the work. The other option is a code generator that spits out the appropriate block into separate file as a partial class definition.

The method delegate argument that the construtor takes is the delegate that the method calls to get its work done.

namespace Droog.Stately
{
[AttributeUsage(AttributeTargets.Method)]
public class StateMethod : Attribute
{
  string methodDelegateName;

  public StateMethod(string methodDelegateName)
  {
    this.methodDelegateName = methodDelegateName;
  }

  public string MethodDelegateName
  {
    get { return methodDelegateName; }
  }
}
}

The StateMethodHandler Attribute

This attribute tags methods as handlers for state methods. As such they must have the same signature (although they can be public, private, protected, whatever) as the actual method, or more to the point, the same signature as the delegate that the state method calls. It's conceivable that the state method adds another argument, although i can't really see why.

There are two types of StateMethodHandlers: Default and state specific.

namespace Droog.Stately
{
[AttributeUsage(AttributeTargets.Method)]
public class StateMethodHandler : Attribute
{
  string methodName;
  byte state;
  bool isDefault;

  public StateMethodHandler(string methodName, byte state)
  {
    this.methodName = methodName;
    this.state = state;
    this.isDefault = false;
  }
  public StateMethodHandler(string methodName)
  {
    this.methodName = methodName;
    this.isDefault = true;
  }

  public bool IsDefault
  {
    get { return isDefault; }
  }

  public string MethodName
  {
    get { return methodName; }
  }

  public byte State
  {
    get { return state; }
  }
}
}

The AStateObject abstract base class

This is a very simplistic implementation. Note that it assumes that the derived class does everything right. I.e. if you forgot the default handler it would die unceremonously, if you had multiple handlers declared for the same things, it would just take the last one, etc.

It also handles only Methods. It should probably be extended to at least properties. But extending it to event handlers could be pretty powerful as well.

The mechanism tries to do as much as possible at first instantiation to both reduce the overhead and allow it to fail early instead of at call time, should the definition be wrong. If performance was a problem, the creation of the delegates could be moved to instantiation as well, and done at each instantiation. It would create an increased overhead for instantiation and a larger memory footprint, so it's a modification that needs to be dictated by use.

namespace Droog.Stately
{
public class AStateObject
{
  /// <summary>
  /// Internal collection for storing the mappings for state delegation. 
  /// </summary>
  private class StateDelegateMap
  {
    MethodInfo[] stateDelegateMethodInfo = new MethodInfo[10];
    MethodInfo invokingMethodInfo;
    FieldInfo delegateFieldInfo;
    MethodInfo delegateMethodInfo;

    public MethodInfo this[byte idx]
    {
      get { CheckBounds(idx); return stateDelegateMethodInfo[idx]; }
      set { CheckBounds(idx); stateDelegateMethodInfo[idx] = value; }
    }

    private void CheckBounds(byte idx)
    {
      if (idx >= stateDelegateMethodInfo.Length)
      {
        MethodInfo[] newStorage = new MethodInfo[idx + 1];
        stateDelegateMethodInfo.CopyTo(newStorage, 0);
        stateDelegateMethodInfo = newStorage;
      }
    }

    public MethodInfo InvokingMethodInfo
    {
      get { return invokingMethodInfo; }
      set { invokingMethodInfo = value; }
    }

    public FieldInfo DelegateFieldInfo
    {
      get { return delegateFieldInfo; }
      set { delegateFieldInfo = value; }
    }

    public MethodInfo DefaultDelegateMethodInfo
    {
      get { return delegateMethodInfo; }
      set { delegateMethodInfo = value; }
    }
  }

  /// <summary>
  /// Internal Dictionary for mapping methods to delegation maps.
  /// This is really just a shorthand for a typed dictionary
  /// </summary>
  private class StateMethodMap
    : Dictionary<string, StateDelegateMap>
  {
  }

  /// <summary>
  /// Static storage for our mappings. Base class stores them all
  /// and lazy-initialized them the first time an instance of
  /// a Type is created
  /// </summary>
  static Dictionary<Type, StateMethodMap> stateDelegationMap
    = new Dictionary<Type, StateMethodMap>();

  protected byte internalStateId;

  protected AStateObject()
  {
    InitStateHandlers();
  }

  /// <summary>
  /// The internal representation of states as bytes. It's up
  /// to the deriving class to decide whether an Accessor should
  /// be exposed and that datatype (recommeneded enum) is used
  /// to represent the class's states.
  /// </summary>
  protected byte InternalStateId
  {
    get { return internalStateId; }
    set
    {
      internalStateId = value;
      InitState();
    }
  }

  /// <summary>
  /// This creates the static mapping of state methods to possible
  /// delegates. It does not create delegates because they are tied
  /// to instances and therefore have to be created for each
  /// instance. This creation happens in <see cref="InitState"/>
  /// </summary>
  private void InitStateHandlers()
  {
    Type t = GetType();
    if (stateDelegationMap.ContainsKey(t))
    {
      // we already generated this map, so we can skip this step
      return;
    }

    MethodInfo[] methods = t.GetMethods(
      BindingFlags.Instance |
      BindingFlags.NonPublic |
      BindingFlags.Public);

    StateMethodMap methodMap = new StateMethodMap();
    stateDelegationMap.Add(t, methodMap);

    // find all state methods for this type
    foreach (MethodInfo m in methods)
    {
      foreach (StateMethod attr 
        in m.GetCustomAttributes(typeof(StateMethod), true))
      {
        FieldInfo delegateField = t.GetField(
          attr.MethodDelegateName,
          BindingFlags.Instance |
          BindingFlags.NonPublic);

        StateDelegateMap delegateMap = new StateDelegateMap();
        delegateMap.InvokingMethodInfo = m;
        delegateMap.DelegateFieldInfo = delegateField;
        methodMap.Add(m.Name, delegateMap);
      }
    }

    // find all state delegates for this type
    foreach (MethodInfo m in methods)
    {
      foreach (StateMethodHandler attr 
        in m.GetCustomAttributes(typeof(StateMethodHandler), true))
      {
        if (methodMap.ContainsKey(attr.MethodName))
        {
          StateDelegateMap delegateMap = methodMap[attr.MethodName];
          if (attr.IsDefault)
          {
            // default method handler
            delegateMap.DefaultDelegateMethodInfo = m;
          }
          else
          {
            // state specific method handler
            delegateMap[attr.State] = m;
          }
        }
      }
    }
  }

  /// <summary>
  /// This gets called every time we change the stateId and creates the
  /// appropriate delegates from the static mapping
  /// </summary>
  private void InitState()
  {
    StateMethodMap methodMap = stateDelegationMap[GetType()];

    foreach (StateDelegateMap delegateMap in methodMap.Values)
    {
      MethodInfo delegateMethodInfo = null;
      if (delegateMap[InternalStateId] != null)
      {
        // got a state specific method, let's map it
        delegateMethodInfo = delegateMap[InternalStateId];
      }
      else
      {
        // no state specific method, use the default
        delegateMethodInfo = delegateMap.DefaultDelegateMethodInfo;
      }
      Delegate stateDelegate = Delegate.CreateDelegate(
        delegateMap.DelegateFieldInfo.FieldType,
        this,
        delegateMethodInfo);

      delegateMap.DelegateFieldInfo.SetValue(this, stateDelegate);
    }
  }
}
}

Now we have a fairly simple framework to create objects in our simulation that can alter their behavior depending on their state. As I start writing code with this framework, it'll probably get fleshed out a bit more and I'll find out whether the design is sustainable, since right now it's a clean-room design more than anything else.

Thursday, February 01, 2007

State-aware programming in C#, part II

Having defined the approach for the stateful framework, let's tackle the design goals: Ideally we would like our code so look something like this:
public class Bot : AStateObject
{
  public virtual void Touch()
  {
    //default implementation
  }

  virtual state(BotStates.Idle)
  {
    public virtual void Touch()
    {
      // Idle specific implementation
    }
  }
}

public class GuardBot : Bot
{
  public override void Touch()
  {
    // override of default implementation
  }

  virtual state(BotStates.Guarding)
  {
    public virtual void Touch()
    {
      // Guarding specific implementation
    }
  }
}
Since that's not possible without altering the language, let's see what we can do to approximate that behavior.

Serializable state-aware objects

I'm just going to get this out of the way first. For the initial version, i'm handling the serializable portion by simple making the abstract base class ISerializable and putting the burden on the subclass to be serializable. .NET already has great facilities for serializing and it won't be hard to retrofit the base class with the methods for automating the saving of state later, as long as we just insist on ISerializable.

Different logic paths per state on state aware methods

Since we can't just create new keywords in the language, what can we do? Well, to start, a large part of the motivation behind stateful programming in UnrealScript can be alleviated by using events. I.e. a common example is a Touch() function. I.e. the object responds to an external event. Well, instead of creating Touch() in different parts of the code, you could just have a Touched event in the class and subscribe and unsubscribe different handlers depending on the state.

While events will play a large part of any complex game logic, the approach for this framework doesn't use events. Instead we use the language feature that makes events possible, i.e. Delegates. Sure we could have a case statement in each stateful Method that branches on the state, but that makes for error-prone and hard to maintain code. Instead each stateful method could simply call a state handler, which is initialized with the appropriate delegate on state change. Now we have a single set of plumbing for setting up all code paths for each state.

But that still leaves a lot of code at state change to attach the right delegates to each method per state change. Instead of doing this manually, we can use Attributes to identify the appropriate handlers and Reflection to discover state methods and the matching state specific handlers.

The resulting code looks like this:

public class Bot : AStateObject
{
  // Start: Definition of the Stateful method
  protected delegate void TouchDelegate();
  protected TouchDelegate touchDelegate;
  [StateMethod("touchDelegate")]
  public void Touch()
  {
    touchDelegate();
  }
  // End: Definition of the Stateful method

  // Tagged as default handler
  [StateMethodHandler("Touch")]
  protected virtual void Default_Touch()
  {
    //default implementation
  }

  // Tagged as Idle state handler
  [StateMethodHandler("Touch", (byte)BotStates.Idle)]
  protected virtual void Idle_Touch()
  {
    // Idle specific implementation
  }
}

public class GuardBot : Bot
{
  // normal override mechanism works in our state code as well
  protected override void Default_Touch()
  {
    // override of default implementation
  }

  // Tagged as Idle state handler
  [StateMethodHandler("Touch", (byte)BotStates.Guarding)]
  protected virtual void Guarding_Touch()
  {
    // Guarding specific implementation
  }
}

This covers pretty much all the bases, but let me just point out the ones specific to state handling.

First we have to separate the state method from its implemenation and then tag it as such. That's how we get:

protected delegate void TouchDelegate();
protected TouchDelegate touchDelegate;
[StateMethod("touchDelegate")]
public void Touch()
{
  touchDelegate();
}

Clearly a lot of busy work for a simple method. But it's busy work a code generator could easily move to a partial class. The StateMethod attribute both tags the method and provides information to our framework which delegate needs to be wired up.

Now we're free to define our state specific implemenation:

[StateMethodHandler("Touch", (byte)BotStates.Idle)]
protected virtual void Idle_Touch()
{
  // Idle specific implementation
}

The name Idle_Touch is convention and does not affect execution. Its purpose serves both to make sure we get a unique method name and that its easily recognizable by someone trying to subclass the class. It's the StateMethodHandler attribute that tells the framework that the method is the Idle handler for Touch(). Why are we casting our state enum to byte? I'll cover that when I talk about type-safe states.

State specific methods should be inherited

Since our state specific methods are really just normal methods that have been tagged as handlers, inheritance proceeds in the regular fashion. Marked as virtual, any subclass can override any state handler. Since Intellisense won't tell you which method is tagged to what state, the naming convention of <StateName>_<StateMethodName> once again becomes useful.

Default and state specific methods

In addition to the StateMethodHandler(string methodName, byte state) constructor, there also exists StateMethodHandler(string methodName). Methods tagged as such are the default handlers:

[StateMethodHandler("Touch")]
protected virtual void Default_Touch()
{
  //default implementation
}

Type-safe states

This last goal is a bit tricky and the solution is only somewhat satisfying. My desire is to have states that are type-safe and discoverable enumerations of states. Naturally enums come to mind. But there are a number of disadvantages to enums:

  1. If we define the enum as part of the framework, all classes that implement the framework have to share a fixed set of states. This is clearly not useful.
  2. If we let a subclass define the enumeration and the framework just stores it as an Id, it solves the first problem. However any further subclassing can only add states by changing the original enum, which is only possible if you are the author of the assembly containing that enumeration.
  3. Even if a subclass decides to throw out the parent's state enum and just create its own, any State property will not permit this, since we can't change the property's signature in the override.

The first we address by hiding state in the bowels of the framework simply as a byte and requiring the subclasses to define their own state enum/code> and casting them. Hence the byte/code> cast in the StateMethodHandler attribute. (If you have more than 256 states, you probably have something a bit more complex than should be handled on simple single valued state changes).

The second is a trade-off for being a compile-time checked framework. I'd rather have my enums to be machine-discoverable and fixed than some dynamic value that only becomes meaningful at runtime. I'll go out on a limb and say that if you are subclassing an existing member of your game's simulation, it should only contain the existing states because the simulation is unlikely to ever set your new states. If you do need more states, maybe your class isn't a subclass at all. You probably have two game objects that share common code, but different states, so they instead have a common ancestor (which doesn't define states). Then your two classes become siblings, each defining their own states.

If you really must extend the states of your base class, you can still handle the last case by simply marking your override of the State accessor as new and taking on the responsibility of doing all the casting.

So that covers the syntax of the implemenentation for a simple stateful framework. Next I'll cover the guts that implements that syntax in State-aware programming in C#, part III.