3

I have written a communication library for an industrial device protocol. The response frame contains a byte array as payload data. The payload data consists of several "objects“ of different types, like integers, floats and other, more complex data like dates or different string encodings. To make this more fun, the data could also be in different byte order.

My current solution is to have a generic data object, which holds the general information like position in the byte array and byte order, and several inherited objects to handle the different data-types:

public abstract class DataObject{
    private readonly int _position;
    public int Position {
        get { return _position; }
    }
    private readonly ByteOrder _byteOrder;
    public abstract object Data { get;}

    /* snip */
}

public class FloatObject : DataObject {
    public abstract object Data {
        get{
            float floatValue;
            /* handle byte to float conversion */
            return floatValue;
        }
    }
}

public class StringObject : DataObject {
    public abstract object Data {
        get{
            string stringValue;
            /* handle byte to string conversion */
            return stringValue;
        }
    }
}

What I do not like about this is the handling of the Data-property. The base class declares it as an object, so every time I need to cast the result to the corresponding type. If I do not declare a Data-property in the base class, but in all the derived classes, I need to cast the data-object to the specific data-object, which is just a shift of the problem. Is there any better design I could use to solve this problem?

  • Had to add the C# tag (and also the language-agnostic tag) so that the code in my answer would be properly formatted. If anyone knows of a better way to achieve the same thing, please feel free to undo this. – Mike Nakis Sep 24 '15 at 15:44
1

If you declare a type that's actually supposed to contain useful state as object, there is really no way to avoid typecasts all over. There's no way around it.

However, it seems that what you're doing, at a higher level, is attempting to extract information from a stream of bytes by applying formal rules to recognize well-defined patterns in the input, and produce data objects from them representing their semantic meaning.

This is a well-studied problem in computer science: parsing. The term is generally used in the context of text, source code for a programming language, but there's no reason you couldn't apply the same techniques to a binary stream. If I had to work with a system as complicated as you're describing, one of the first things I'd do is write a parser for it.

Exactly how to go about doing that depends heavily on how the data stream is defined, but you can probably use existing tools such as Protocol Buffers, or a parser-generator like ANTLR to take care of a lot of the heavy lifting.

0

I doubt anything can be done around your current design to avoid casting.

When developing from scratch, I would recommend to avoid creation of too abstractly typed fields in classes, responsible for protocol handling. Remember LSP!

Instead of inventing your own protocol format and serializers/deserializer, it would be interesting to look at Google Protocol Buffers or gRPC.

0

Have you examined this possibility?

public abstract class DataObject<T>
    public byte[] RawData { get;}
    public abstract object DataAsObject { get;}
    public abstract T Data { get;}
public class FloatObject : DataObject<float>
    public float Data { get { ... } }
public class StringObject : DataObject<string>
    public string Data { get { ... } }

Is there any reason why it would not work, that I cannot see right now?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.