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)
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.
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.
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());
}
}