To convert any value types (not just primitive types) to byte arrays and vice versa:
public T FromByteArray<T>(byte[] rawValue)
{
GCHandle handle = GCHandle.Alloc(rawValue, GCHandleType.Pinned);
T structure = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return structure;
}
public byte[] ToByteArray(object value, int maxLength)
{
int rawsize = Marshal.SizeOf(value);
byte[] rawdata = new byte[rawsize];
GCHandle handle =
GCHandle.Alloc(rawdata,
GCHandleType.Pinned);
Marshal.StructureToPtr(value,
handle.AddrOfPinnedObject(),
false);
handle.Free();
if (maxLength < rawdata.Length) {
byte[] temp = new byte[maxLength];
Array.Copy(rawdata, temp, maxLength);
return temp;
} else {
return rawdata;
}
}
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.
Best Answer
Primitive types are easy because they have a defined representation as a byte array. Other objects are not because they may contain things that cannot be persisted, like file handles, references to other objects, etc.
You can try persisting an object to a byte array using
BinaryFormatter
:But not all types are serializable. There's no way to "store" a connection to a database, for example. You can store the information that's used to create the connection (like the connection string) but you can't store the actual connection object.
UPDATE
BinaryFormatter
is now considered "insecure", especially when trying to deserialize data from untrusted sources. Which makes sense, since you're essentially loading raw binary data into memory that could do bad things.Suggested alternatives are to use a non-binary format like XML or JSON for generic object graphs, or write custom binary serialization code to serialize primitive members explicitly.