Sunday, May 14, 2006

Type and C# and Switching

Ladies, gentlemen, sometimes we want to switch on type but we can't do it.

Let's say you have a messaging system and you want to send messages to your entities. Also you want to be able to add as many messages as you want without changing any code. Well then you might use a message or dispatch system.


switch(entityMessage.GetType())
{
case typeOf(MessagePoke):
{
MessagePoke poke = (MessagePoke)entityMessage;

if(poke.PokedBy == "A magic wand")
{
TurnIntoAFrog();
}
}

case typeOf(messageTalkTo)...
case typeOf(messageSetOnFire)...
case typeOf(messageMusicPlaying)...
case typeOf(messageWeatherConditions)...

default:
{
//just ignore the message
}
}


The entity just chooses which messages it will recognise and handles them. (I mentioned this in passing on a comment on a gamedev blog by Trapper Zoid - here. See the very last comment by Balaam.)

So let's say you want the above - where messages are classes and you want to handle certain messages according to whatever class they are. Well then we want to swap on type but in C# you can't swap on type.

We can't swap on type? Why? Well there is a fine article that explains why here.

So how can we do the switch? We can switch on strings, so we can switch on the class name and possibly the namespace if we want to support lots and lots of message like the below code:


interface Messsage { ... }

namespace WeaponMessages
{
touchedBy : Message { ... }
}

namespace SocialMessages
{
touchedBy : Message { ... }
}


So if we want the full namespace of the object.


object.GetType().FullName; //WeaponMessages.TouchedBy [lots of extra information]


If we want the just the name


object.GetType().Name; //TouchedBy


Are we having fun yet?

If we want the string from the actually class then we code, like so.


typeOf(TouchedBy).FullName;
typeOf(TouchedBy).Name;


And with that we can form a dispatch or message system. Let's rewrite the the example we had at the start.


switch(enityMessage.GetType().Name)
{
case "MessagePoke":
{
MessagePoke poke = (MessagePoke)enityMessage;

if(poke.PokedBy == "A magic wand")
{
TurnIntoAFrog();
}
}

case "MessageTalkTo"...
case "MessageSetOnFire"...
case "MessageMusicPlaying"...
case "MessageWeatherConditions"...

default:
{
//just ignore the message
}
}


What's with the constant strings? Why don't we write typeof(messageTalkTo).Name? Well, it's not "constant", it could change! Therefore we need to write out the strings.

And that's how you could do a dispatch message system. Next post will probably be why you might want a message system.

1 comment:

balaam said...

I've just been playing with this and I just wanted to add - if you want your messages to return a value.

NPC.SendMessage(new FindHealth());

You can add an anonymous delegate.

int whatsTheHealth = -1;
NPC.SendMessage(new FindHealth(new delegate(int thisIsTheHealth)
{
whatsTheHealth = thisIsTheHealth;
}));

// carry on

or you can deal with the code in the delegate or continue in a new function whatever. Quite a powerful technique.