views:

157

answers:

4

Hi,

I am storing data with different formats and lengths. I have a class hierarchy to represent this:

abstract class BaseDataFormat{ 
    abstract void InitalizeFromBytes(byte [] );
}

class DataFormat1 : BaseDataFormat{...}  // data stored in 3 bytes
class DataFormat2 : BaseDataFormat{...} /// data stored in 4 bytes

When I am reading my data (say from a byte []), I need to know the length (# of bytes) to read and create the corresponding type appropriately. DataFormat1 and DataFormat2 have different lengths, so how do I get this information at runtime? ie.

Fcn<DATAFORMATTYPE>(...)
 where DATAFORMATTYPE: BaseDataFormat, new();
{

DATAFORMATTYPE tmp = new DATAFORMATTYPE();
tmp.InitalizeFromBytes(ReadFromByteBuffer( ... someLength));

}

How do I encode the proper # of bytes to read based on the DATAFORMATTYPE? The length for each feels like it should be a static property of the data format type, but static properties cant be overriden by the derived class, so I am not sure how to do this.

The length can be coded as an instance property, but this seems like it should be knowledge encoded at the class level(ie. static). Is there a design pattern to solve this?

Thanks

A: 

The best if you REALLY want to enforce that it is a property of the class and not of the instance is to use an Attribute on the class.

But you will need to build a cache from that because reflection is prettly slow compared to a direct access.

[AttributeUsage(AttributeTargets.Class)]
class ByteCountAttribute : Attribute
{
    public int Value { get; private set; }
    public ByteCountAttribute(int count)
    {
        Value = count;
    }
}

[ByteCount(5)]
class DataFormat1 : BaseDataFormat{}  // data stored in 3 bytes
[ByteCount(6)]
class DataFormat2 : BaseDataFormat{} /// data stored in 4 bytes

static Dictionary<Type, int> s_typeCache = new Dictionary<Type,int>();
public static int GetByteCount<T>() where T : BaseDataFormat
{
    int result;
    var type = typeof(T);
    if (!s_typeCache.TryGetValue(type, out result))
    {
        var atts = type.GetCustomAttributes(typeof(ByteCountAttribute), false);
        result = ((ByteCountAttribute)atts[0]).Value;
        s_typeCache.Add(type, result);
    }
    return result;
}

(The code have no error management but it work)

As said by darkassassin93 one of the problem with this approach is that you can't enforce it's presence and so you have to document that this attribute is necessary contrary to an abstract property where the compiler does the job for you.

VirtualBlackFox
+2  A: 

Perhaps have a property in BaseDataFormat called DataLength. To force all inheritors to set a value, take the length in at BaseDataFormat's constructor, and then set that property to the data length.

Example:

abstract class BaseDataFormat
{ 
    BaseDataFormat(int dataLength)
    {
        DataLength = dataLength;
    }

    public int DataLength { get; private set; }
    abstract void InitalizeFromBytes(byte [] );
}

class DataFormat1 : BaseDataFormat
{
    public DataFormat1() : base(3)
    {
        // ...
    }
}

Granted, it's not at the static level, but it is something that is forced for all inheritors.

Another way is as how VirtualBlackFox suggested, decorate the class with an attribute. The only problem is that AFAIK you can't force attributes onto a class, like with abstract members.

nasufara
+1  A: 

I would build Factory objects for each DataFormat. Or maybe make DataFormat be the factory, and call the actual data objects Datum or something like that. Then you can instantiate the factory, and pass it the byte buffer, and it can read the necessary bytes and construct the actual data object.

Daniel Pryden
I like this approach. Good pattern for when you don't want to get tied up in the details of instantiation; and can just pass it the byte buffer and let it figure it out.
Gurdas Nijor
Whatever the internal solution to the specific question (where to store the byte count) is, the factory pattern is pretty obvious to have.
VirtualBlackFox
+2  A: 

How do you even know what subclass to instantiate when reading data? A more generic solution for creating objects from serial data is to use the factory pattern.

An example of this is available at: http://en.wikipedia.org/wiki/Factory%5Fmethod%5Fpattern#Encapsulation

jakber