2

I have a base class which handles messages received from a stream. Now I want to check if a new message received, which one it is and depending on this how to handle this message.

Then I head the idea, the code would be much better to read if I create one Base class for all Messages and a lot of derived class for the individual messages. (I have about 100 different Messages to handle)

The Problem here: We can't convert a base class to a derived class (see Convert base class to derived class) This is total logical, and I understand why it has to be like this. But I acutally don't need a "whole" class, I just need a property which is named differently, depending on which message received, which parses the data from the base class. But I also don't want a class whith every property written like public byte Message02_Value1 => Data[0]. I would like to use this Syntax: MessageBaseClass.Message01.Value

I could also write code while it's getting the data from the stream

if (function == 1) new MessageBaseClass.Message01(); if (function == 2) new MessageBaseClass.Message02();

but this feels like double work..

So my question is: How can I use properties which parse data from a base class, depending on what kind of content is in this class?

To clearify what I want i wrote this code (which actually doesn't work)

class Program
    {
        static void Main(string[] args)
        {

            Msg[] messages =
                {
                    new Msg { Function = 1, Data = new byte[] { 1, 2, 3, 4 } } ,
                    new Msg { Function = 2, Data = new byte[] { 1, 2, 3, 4 } }
                };

            foreach (Msg msg in messages)
            {
                switch (msg.Function)
                {
                    case 1:
                        var Message1 = msg as Msg.Message01;//Error, is not able to convert --> Message1 == null
                        Console.WriteLine($"Serial: {Message1.Serial}");
                        break;
                    case 2:
                        var Message2 = msg as Msg.Message02;//Error, is not able to convert --> Message2 == null
                        Console.WriteLine($"Value1: {Message2.Value1}" +
                            $"Value2: {Message2.Value2}" +
                            $"Value3: {Message2.Value3}");
                        break;
                }
            }
        }

        class Msg
        {
            public byte Function { get; set; }
            public byte[] Data { get; set; }

            public class Message01 : Msg
            {
                public uint Serial => BitConverter.ToUInt32(Data, 0);
            }

            public class Message02 : Msg
            {
                public byte Value1 => Data[0];
                public byte Value2 => Data[1];
                public ushort Value3 => BitConverter.ToUInt16(Data, 2);
            }
        }
    }
Max R.
  • 811
  • 1
  • 13
  • 31
  • So you are aiming to have around 100 derived class in your code ? Maybe it is me but it sure sounds like there is a better way to it than that .. – Picnic8 Oct 17 '17 at 14:47
  • I think there is nothing wrong with the `if (func...` you had, just make it into message factory that will convert your stream into message instances and you're good imho. and then you can use inheritance or just an interface for your message classes that will have something like `.showMeData()` and you're good, this way your factory is the only place caring about what class you're instantiating. – user3012759 Oct 17 '17 at 14:47
  • It might be better for maintenance sake to generate the code. Even if you/we come up with a great design, generating the code will reduce the impact of a message change considerably – Emond Oct 17 '17 at 14:59

1 Answers1

4

The approach that you are trying to take is fundamentally wrong, for the same reasons that it is wrong to switch on the runtime type of your class. Essentially, you are doing the same thing, replacing a type with its numeric code.

C# offers a very good option for processing subclasses without dispatching on the type explicitly: make overloads for individual types, cast message to dynamic, and let C# call the right overload for you:

void ProcessMessages(IEnumerable<Msg> messages) {
    foreach (var m in messages) {
        ProcessSingleMessage((dynamic)m);
    }
}

void ProcessSingleMessage(Message1 m1) {
    // Access properties of m1 as needed
}
void ProcessSingleMessage(Message2 m2) {
    // Access properties of m2 as needed
}
...
// Catch-all handler
void ProcessSingleMessage(Msg m) {
    throw new InvalidOperationException("Received a message of unknown type: "+m.GetType());
}
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523