Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
381 views
in Technique[技术] by (71.8m points)

c# - 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;
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...