プロトタイプTCP接続を作成していますが、送信するデータの均質化に問題があります。
現時点では、文字列のみを送信していますが、将来は任意のオブジェクトを送信できるようにしたいと考えています。
すべてがバイト配列にキャストできると思ったので、コードは現時点では非常に単純です。
void SendData(object headerObject, object bodyObject)
{
byte[] header = (byte[])headerObject; //strings at runtime,
byte[] body = (byte[])bodyObject; //invalid cast exception
// Unable to cast object of type 'System.String' to type 'System.Byte[]'.
...
}
もちろんこれは簡単に十分に解決されます
if( state.headerObject is System.String ){...}
問題は、そのようにすると、実行時にbyte []にキャストできないすべてのタイプのオブジェクトをチェックする必要があることです。
実行時にbyte []にキャストできないすべてのオブジェクトを知っているわけではないため、これは実際にはオプションではありません。
C#.NET 4.0でオブジェクトをバイト配列に変換するにはどうすればよいですか?
BinaryFormatter
を使用します。
byte[] ObjectToByteArray(object obj)
{
if(obj == null)
return null;
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream())
{
bf.Serialize(ms, obj);
return ms.ToArray();
}
}
obj
およびobj
内のすべてのプロパティ/フィールド(およびそれらのすべてのプロパティ/フィールドなど)に Serializable
属性 でタグ付けする必要があることに注意してください。
この記事をご覧ください: http://www.morgantechspace.com/2013/08/convert-object-to-byte-array-and-vice.html
以下のコードを使用してください
// Convert an object to a byte array
private byte[] ObjectToByteArray(Object obj)
{
if(obj == null)
return null;
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, obj);
return ms.ToArray();
}
// Convert a byte array to an Object
private Object ByteArrayToObject(byte[] arrBytes)
{
MemoryStream memStream = new MemoryStream();
BinaryFormatter binForm = new BinaryFormatter();
memStream.Write(arrBytes, 0, arrBytes.Length);
memStream.Seek(0, SeekOrigin.Begin);
Object obj = (Object) binForm.Deserialize(memStream);
return obj;
}
前に他の人が言ったように、バイナリシリアル化を使用できますが、余分なバイトが生成されるか、まったく同じデータではないオブジェクトに逆シリアル化される可能性があります。一方、反射の使用は非常に複雑で非常に遅いです。オブジェクトを厳密にバイトとその逆に厳密に変換できる別のソリューションがあります-マーシャリング:
var size = Marshal.SizeOf(your_object);
// Both managed and unmanaged buffers required.
var bytes = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
// Copy object byte-to-byte to unmanaged memory.
Marshal.StructureToPtr(your_object, ptr, false);
// Copy data from unmanaged memory to managed buffer.
Marshal.Copy(ptr, bytes, 0, size);
// Release unmanaged memory.
Marshal.FreeHGlobal(ptr);
そして、バイトをオブジェクトに変換するには:
var bytes = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.Copy(bytes, 0, ptr, size);
var your_object = (YourType)Marshal.PtrToStructure(ptr, typeof(YourType));
Marshal.FreeHGlobal(ptr);
フィールドごとに独自のシリアル化フィールドと比較して小さなオブジェクトと構造体にこのアプローチを使用するのは著しく遅く、部分的に安全ではありません(アンマネージメモリから/への二重コピーのため)、シリアル化を実装せずにオブジェクトを厳密にbyte []に変換する最も簡単な方法です[Serializable]属性なし。
探しているのはシリアル化です。 .Netプラットフォームでは、いくつかの形式のシリアル化を利用できます
byte[]
に簡単に変換可能な文字列を生成しますpublic static class SerializerDeserializerExtensions
{
public static byte[] Serializer(this object _object)
{
byte[] bytes;
using (var _MemoryStream = new MemoryStream())
{
IFormatter _BinaryFormatter = new BinaryFormatter();
_BinaryFormatter.Serialize(_MemoryStream, _object);
bytes = _MemoryStream.ToArray();
}
return bytes;
}
public static T Deserializer<T>(this byte[] _byteArray)
{
T ReturnValue;
using (var _MemoryStream = new MemoryStream(_byteArray))
{
IFormatter _BinaryFormatter = new BinaryFormatter();
ReturnValue = (T)_BinaryFormatter.Deserialize(_MemoryStream);
}
return ReturnValue;
}
}
以下のコードのように使用できます。
DataTable _DataTable = new DataTable();
_DataTable.Columns.Add(new DataColumn("Col1"));
_DataTable.Columns.Add(new DataColumn("Col2"));
_DataTable.Columns.Add(new DataColumn("Col3"));
for (int i = 0; i < 10; i++) {
DataRow _DataRow = _DataTable.NewRow();
_DataRow["Col1"] = (i + 1) + "Column 1";
_DataRow["Col2"] = (i + 1) + "Column 2";
_DataRow["Col3"] = (i + 1) + "Column 3";
_DataTable.Rows.Add(_DataRow);
}
byte[] ByteArrayTest = _DataTable.Serializer();
DataTable dt = ByteArrayTest.Deserializer<DataTable>();
Encoding.UTF8.GetBytes
の使用は、MemoryStram
の使用よりも高速です。ここでは、入力オブジェクトをJSON文字列に変換し、JSON文字列からバイトを取得するためにNewtonsoftJsonを使用しています。
byte[] SerializeObject(object value) =>Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value));
Method | Mean | Error | StdDev | Median | Gen 0 | Allocated |
-------------------------- |---------:|----------:|----------:|---------:|-------:|--
ObjectToByteArray | 4.983 us | 0.1183 us | 0.2622 us | 4.887 us | 0.9460 | 3.9 KB |
ObjectToByteArrayWithJson | 1.548 us | 0.0309 us | 0.0690 us | 1.528 us | 0.3090 | 1.27 KB |
拡張機能クラスの組み合わせソリューション:
public static class Extensions {
public static byte[] ToByteArray(this object obj) {
var size = Marshal.SizeOf(data);
var bytes = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(data, ptr, false);
Marshal.Copy(ptr, bytes, 0, size);
Marshal.FreeHGlobal(ptr);
return bytes;
}
public static string Serialize(this object obj) {
return JsonConvert.SerializeObject(obj);
}
}
このようなシンプルなものはどうですか?
return ((object[])value).Cast<byte>().ToArray();
オブジェクトをバイト配列に変換する代替方法:
TypeConverter objConverter = TypeDescriptor.GetConverter(objMsg.GetType());
byte[] data = (byte[])objConverter.ConvertTo(objMsg, typeof(byte[]));
むしろ、「バイトにキャストする」よりも「シリアル化」という表現を使用します。オブジェクトをシリアル化するということは、オブジェクトを再構築するためにリモートボックスで使用できるバイト配列(またはXMLなど)に変換することを意味します。 .NETでは、 Serializable
属性 は、オブジェクトをシリアル化できる型をマークします。
フレームワークで 組み込みのシリアル化ツール を使用し、 MemoryStream にシリアル化できます。これは最も簡単なオプションかもしれませんが、シナリオに厳密に必要な場合よりも大きいbyte []を生成する可能性があります。
その場合、リフレクションを使用して、シリアル化されるオブジェクトのフィールドやプロパティを反復処理し、それらを手動でMemoryStreamに書き込み、必要な場合は非自明な型をシリアル化するために再帰的にシリアル化を呼び出します。このメソッドはより複雑で、実装に時間がかかりますが、シリアル化されたストリームをより詳細に制御できます。
Newtonsoft.JsonバイナリJSONを使用し、[Serializable]属性ですべてをマークする必要のない追加実装。唯一の欠点は、オブジェクトを匿名クラスでラップする必要があることです。そのため、バイナリシリアル化で取得したバイト配列は、これとは異なる場合があります。
public static byte[] ConvertToBytes(object obj)
{
using (var ms = new MemoryStream())
{
using (var writer = new BsonWriter(ms))
{
var serializer = new JsonSerializer();
serializer.Serialize(writer, new { Value = obj });
return ms.ToArray();
}
}
}
BSONはクラスまたは配列で開始する必要があるため、匿名クラスが使用されます。私はbyte []をオブジェクトにデシリアライズしようとはしていませんが、動作するかどうかはわかりませんが、byte []への変換速度をテストし、私のニーズを完全に満たしています。