remdao
remdao

Reputation: 905

Convert an array of different value types to a byte array

This is what I've come up with so far, but it doesn't seem very optimal, any ideas on better approaches?

public void ToBytes(object[] data, byte[] buffer)
{
    byte[] obytes;
    int offset = 0;

    foreach (object obj in data)
    {
        if (obj is string)
            obytes = System.Text.Encoding.UTF8.GetBytes(((string)obj));
        else if (obj is bool)
            obytes = BitConverter.GetBytes((bool)obj);
        else if (obj is char)
            obytes = BitConverter.GetBytes((char)obj);
        // And so on for each valuetype

        Buffer.BlockCopy(obytes, 0, buffer, offset, obytes.Length);
        offset += obytes.Length;
    }
}

Upvotes: 1

Views: 3821

Answers (3)

Beatles1692
Beatles1692

Reputation: 5320

You can use a StreamWriter to write to a memory stream and use its buffer:

  {
               byte[] result;
            using (MemoryStream stream = new MemoryStream())
            {
                StreamWriter writer = new StreamWriter(stream);
                writer.WriteLine("test");
                writer.WriteLine(12);
                writer.WriteLine(true);

                writer.Flush();

                result = stream.GetBuffer();
            }

            using(MemoryStream stream=new MemoryStream(result))
            {
                StreamReader reader = new StreamReader(stream);
               while(! reader.EndOfStream)
                 Console.WriteLine(reader.ReadLine());
               }
            }

Upvotes: 0

Mehrdad Afshari
Mehrdad Afshari

Reputation: 422006

Probably, you should consider using BinaryFormatter instead:

var formatter = new BinaryFormatter();
var stream = new MemoryStream();
formatter.Serialize(stream, obj);
byte[] result = stream.ToArray();

Beside that, there are some pretty good serialization frameworks like Google Protocol Buffers if you want to avoid reinventing the wheel.

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1500735

Well, you could have a map like this:

private static readonlyDictionary<Type, Func<object, byte[]>> Converters = 
    new Dictionary<Type, Func<object, byte[]>>()
{
    { typeof(string), o => Encoding.UTF8.GetBytes((string) o) },
    { typeof(bool), o => BitConverter.GetBytes((bool) o) },
    { typeof(char), o => BitConverter.GetBytes((char) o) },
    ...
};

public static void ToBytes(object[] data, byte[] buffer)
{
    int offset = 0;

    foreach (object obj in data)
    {
        if (obj == null)
        {
            // Or do whatever you want
            throw new ArgumentException("Unable to convert null values");
        }
        Func<object, byte[]> converter;
        if (!Converters.TryGetValue(obj.GetType(), out converter))
        {
            throw new ArgumentException("No converter for " + obj.GetType());
        }

        byte[] obytes = converter(obj);
        Buffer.BlockCopy(obytes, 0, buffer, offset, obytes.Length);
        offset += obytes.Length;
    }
}

You're still specifying the converter for each type, but it's a lot more compact than the if/else form.

There are various other ways of constructing the dictionary, btw. You could do it like this:

private static readonly Dictionary<Type, Func<object, byte[]>> Converters = 
        new Dictionary<Type, Func<object, byte[]>>();

static WhateverYourTypeIsCalled()
{
    AddConverter<string>(Encoding.UTF8.GetBytes);
    AddConverter<bool>(BitConverter.GetBytes);
    AddConverter<char>(BitConverter.GetBytes);
}

static void AddConverter<T>(Func<T, byte[]> converter)
{
    Converters.Add(typeof(T), x => converter((T) x));
}

I see another answer has suggested binary serialization. I'm personally not keen on "opaque" serialization schemes like that. I like to know exactly what's going to be in the data in a way that means I can port it to other platforms.

I would point out, however, that your current scheme doesn't give any sort of delimiter - if you have two strings, you'd have no idea where one stopped and the other started, for example. You also don't store the type information - that may be okay, but it may not be. The variable length issue is usually more important. You might consider using a length-prefix scheme, like the one in BinaryWriter. Indeed, BinaryWriter may well be a simpler solution in general. You'd probably want to still have a map of delegates, but make them actions taking a BinaryWriter and a value. You could then build the map by reflection, or just a hardcoded list of calls.

Then you'd just initialize a BinaryWriter wrapping a MemoryStream, write each value to it appropriately, then call ToArray on the MemoryStream to get the results.

Upvotes: 4

Related Questions