将不同值类型的数组转换为字节数组
Posted
技术标签:
【中文标题】将不同值类型的数组转换为字节数组【英文标题】:Convert an array of different value types to a byte array 【发布时间】:2010-11-26 00:42:16 【问题描述】:这是我迄今为止提出的,但似乎不是很理想,有什么更好的方法的想法吗?
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;
【问题讨论】:
【参考方案1】:嗯,你可以有这样的地图:
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;
您仍在为每种类型指定转换器,但它比 if/else 形式紧凑得多。
顺便说一句,还有各种其他方法可以构建字典。你可以这样做:
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));
我看到另一个答案建议二进制序列化。我个人并不热衷于这样的“不透明”序列化方案。我想确切地知道数据中的内容,这意味着我可以将其移植到其他平台。
但是,我要指出,您当前的方案没有给出任何类型的分隔符 - 例如,如果您有两个字符串,您将不知道一个在哪里停止而另一个在哪里开始。您也不存储类型信息 - 这可能没问题,但可能不是。可变长度问题通常更为重要。您可以考虑使用长度前缀方案,例如 BinaryWriter
中的方案。实际上,BinaryWriter
通常可能是一个更简单的解决方案。您可能仍希望拥有一个代表地图,但让他们采取BinaryWriter
和一个值的操作。然后,您可以通过反射构建地图,或者只是一个硬编码的调用列表。
然后你只需初始化一个包装MemoryStream
的BinaryWriter
,将每个值适当地写入其中,然后在MemoryStream
上调用ToArray
以获取结果。
【讨论】:
更重要的是,当前方案不提供任何类型的信息。例如,您将不知道您正在阅读的内容是integer
还是 float
。
@Mehrdad:这可能是个问题,但也可能不是。类型信息可能是隐含的——例如对象可以是类型中字段的值,其中整体类型是预先知道的。
感谢您的回答。我知道可变长度问题,但试图限制这个问题。我一直在使用的解决方案是将每个对象的长度作为短裤存储在一个单独的字节数组中,我在转换后将其复制到缓冲区。客户端和服务器都知道类型信息,因此这不是问题。您的解决方案看起来不错,因为服务器需要能够与 .NET 客户端和 Java 客户端进行通信。【参考方案2】:
或许,您应该考虑改用BinaryFormatter
:
var formatter = new BinaryFormatter();
var stream = new MemoryStream();
formatter.Serialize(stream, obj);
byte[] result = stream.ToArray();
除此之外,如果您想避免重新发明***,还有一些非常好的序列化框架,例如 Google Protocol Buffers。
【讨论】:
谢谢。如果还有一种方法可以反序列化 Java 中的数据,我会使用 BinaryFormatter。我将研究谷歌协议缓冲区。不知道有没有Java版? remdao: 是的,它有一个 Java 版本:code.google.com/p/protobuf 如果你想要一个跨平台的序列化代码,Jon 的另一个 C# 实现更适合这种情况:code.google.com/p/protobuf-csharp-port 【参考方案3】:您可以使用 StreamWriter 写入内存流并使用其缓冲区:
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());
【讨论】:
以上是关于将不同值类型的数组转换为字节数组的主要内容,如果未能解决你的问题,请参考以下文章