views:

302

answers:

6

I'm implementing a system to send Messages between different parts of a program I'm writing. There are some generic message types as well as some specific to each part of the program. I would like to avoid the hierarchy rot inherent in deriving from a base message class for each type, So i'm encapsulating this type in an int or ushort. Then, I centralize the different types with a "Messages" namespace, and a static class with a bunch of constants. However, I ran into the issue of having to maintain a list of unique numbers for each different section:

namespace Messages
{
    public static class Generic
    {
        public const Int32 Unknown = 0;
        public const Int32 Initialize = 1;
        ...
        public const Int32 Destroy = 10;
    }
}

Then elsewhere

namespace Messages
{
    public static class Graphics
    {
        public const Int32 Unknown = 0;
        public const Int32 AddGraphic = 11; // <-- ?
    }
}

Having that arbitrary 11 seems difficult, especially if I have several of these, maintaining and updating to make sure there are no collisions seems to be a pain. Is there an easy solution in order to make sure each reference to this is unique? I tried using static readonly, initializing them off of a Unique.ID() function in a static constructor, but if I do that I am unable to switch() over the passed Message type, as it says "A constant type is expected" for each case.

+7  A: 

Is there some reason you aren't using enums?

public enum MessageTypes
{
    Unknown,
    Initialize,
    ...
}

-- Edit:

Elaborating on my comment, Consider

enum MessageType
{
    Update,
    Delete,
    Destroy
}

MessageType t = ...;

switch(t){
   case MessageType.Update:
       DoUpdate();
   }
}

Versus:

interface IActionable
{
   void Do ();
}


public abstract class ActionableBase : IActionable
{
   // some other things

   public abstract void Do ();
}


public class UpdateAction : ActionableBase
{
   public override void Do ()
   {
       // Update Code
   }
}

...

IActionable a = ...;
a.Do();
Noon Silk
An enum wouldn't allow me to "shard" the declaration of different message types. switch(message.Type) { case Messages.Generic.Initialize:break; case Messages.Graphics.AddGraphic:break; }
Quantumplation
(er, the formatting on that came out weird but i'm sure you get the idea.)
Quantumplation
You said in your post you didn't want a hierarchy of classes, now you want one? To be honest, if your switch statement is that large, I'd say you should probably think about how to make your design more OO.
Noon Silk
Well, I want a Hierarchy of types, but not classes. Originally I had a base message class and derived from it, and found the number of classes I had exploding beyond any kind of maintainability. Plus, all the casts to get the Message m that was passed into the correct subtype was getting cumbersome.
Quantumplation
I really think you'd be better off reconsidering your design, in general. In C#, you really shouldn't be having any `int` constants, for the purposes of message identification. You should generally use an `enum`, for such things, or some form of inheritance/action system.
Noon Silk
@Damian ints (or enums) are never an appropriate solution to an unmanageable number of subclasses.
Rex M
First of all concepts of OOPS was designed to reduce switch and if kind of statements. Instead of casting and switch statements you can redsign using genetics and good hierarchy. Carefully designed virtual and overridden methods are faster as well as easy to maintain.
Akash Kava
@silky: That is what I am trying to do, really. =/@Rex M: I am not dead set on ints or enums. I simply removed the inheritance from my design and tackled it by allowing the base message type to store information on it's purpose. My first instinct was to do this with enum's (and then later const ints). I'm open to suggestions though.
Quantumplation
@Akash: I'm not sure I understand your meaning.
Quantumplation
Damian: What decisions are you making on the basis of these enums? The way you would swap it to an inheritance scheme, is by abstracting out these decisions to a general 'action' (`ISomeActable`) and then providing concrete implementations of this, perhaps from some base, `SomeActableBase`.
Noon Silk
The only problem I see with abstracting the actions into their own interface/classes, is that the class receiving the messages (Agents, for a concurrent framework) are the ones that contain the data for the actions. Agents receive messages, and when they obtain thread time they process them. Separating the actions from the data seems like it would introduce needless complexity.
Quantumplation
You pass the data into the actions.
Noon Silk
Bit of a resurrection, but I worked on a different project for a few months and came back and reconsidered this problem and the advice of the people here, and realize I was just being silly. ><
Quantumplation
+1  A: 

You can use a number range for each class. Define a base number for the class and add 0, 1, 2, etc to that base number.

Eric J.
+1  A: 

If you want to keep them numeric one way is to divide them into different magnitudes:

namespace Messages
{
    public static class Generic
    { 
        // these messages are 3-figure numbers
        public const Int32 Unknown = 0;
        public const Int32 Initialize = 101;
        ...
        public const Int32 Destroy = 110;
    }

    public static class Graphics
    {
        // these messages are 4-figure numbers
        public const Int32 Unknown = 0;
        public const Int32 AddGraphic = 1001; // <-- ?
        // and so on...
    }

}

Then you just need to make sure that you keep within the boundaries for each type of message.

Fredrik Mörk
+1  A: 

This isn't automatic, but it may be a bit easier to maintain then copying the values everywhere:

 public enum Generic
 {
  Unknown = 0,
  Initialize = 1,
  Destroy = 10
 }

 public enum Graphics
 {
  AddGraphic = Generic.Destroy + 1
 }

So, you can have all of your specific enums start with the value from a previous enum set and build them up like that.

In your actual objects, you could store them as int's and just convert whatever enum value to the appropriate int.

Although, it seems that inheritance may be inevitable in this case since there is a natural hierarchy in your data model.

TJB
Why wouuldn't AddGraphic just be another option in the Generic enum?
Taylor Leese
Taylor: I'm looking to decentralize the message types, so that different systems define their own message types.
Quantumplation
@Taylor so he can pass both Generic.Value or Graphics.Value into the same method and not have overlapping values (e.g. Generic.Unknown and Graphics.AddGraphic would both be 0)
Rex M
I still don't really see why it wouldn't be one enum or perhaps switch on the system type and then the individual systems enum. This way you wouldn't have to worry about them being unique for all systems.
Taylor Leese
@Taylor L I'm not sure exactly what his case is, but in my experience if you can put relevant data / values along with the code that uses it, it makes the codebase much more manageable. Less switching constantly between files and scrolling through long lists of constant strings / enums which may have very little to do with each other. I do tend to agree that separate enums + hierarchy of classes may be a better approach, but there's some relationship between them he wants to maintain.
TJB
A: 

I suggest you lookup the difference between 'command' & 'message', this might help you come to the conclusion that the use of magic numbers\enums inside messages is a bad idea.

Ideally you want to create 'commands' that are observed and actioned by listeners...

HTH

Ollie

AWC
A: 

If you really, really want to do this, you can create one generic private Enum that holds all of the possible values.

You can then expose those values through your separate classes as read-only properties that expose the enums as Int32s - e.g.

namespace Messages
{
    private enum AllMessageTypes
    {
        Update,
        Delete,
        Destroy,
        AddGraphic 
    }

    public static class Generic
    {
        public Int32 Update 
        {
            get { return (Int32)AllMessageTypes.Update; }
        }
        ...
    }

    public static class Graphics
    {
        public Int32 AddGraphic 
        {
            get { return (Int32)AllMessageTypes.AddGraphic ; }
        }
    }
}

However - I'd recommend you redesign your solution. This seems to be asking for trouble (as I'm sure people will comment on)

Chris Gill
Quantumplation