2017-12-05 9 views
2

これは簡単ですが、わかりにくいです。私の質問は、私は構造体を持っていると私はパディングやメタデータのための追加のバイトなしでバイトストリームに変換する必要があります。私は構造体を持っていると仮定しシリアライゼーションやパディングなしでオブジェクトをバイト配列に変換する

[StructLayout(LayoutKind.Sequential)] 
public struct MyStruct 
{ 
    public ushort a;     
    public uint b; 
    public uint c; 
    public ushort d; 
} 

備考:私はパディングが

を追加ありますようにを動作しません下記の溶液 を使用して、(プロジェクトが制約)1に、ここでパックを変更することはできません
int size = Marshal.SizeOf(typeof(T)); 
byte[] arr = new byte[size]; 
IntPtr ptr = Marshal.AllocHGlobal(size); 
Marshal.StructureToPtr(str, ptr, true); 
Marshal.Copy(ptr, arr, 0, size); 
Marshal.FreeHGlobal(ptr); 

備考:メタデータが追加されているため、バイナリ形式のシリアル化は使用できません。私は単に何をしたいすべての場合= 1、B = 2、C = 3、D = 4 GENERIC WAY

arr {byte[12]} byte[] 
    [0] 1 byte 
    [1] 0 byte 
    [2] 2 byte 
    [3] 0 byte 
    [4] 0 byte 
    [5] 0 byte 
    [6] 3 byte 
    [7] 0 byte 
    [8] 0 byte 
    [9] 0 byte 
    [10] 4 byte 
    [11] 0 byte 

すべてのヘルプ

ようにバイナリ形式を取得するには?

+0

「BitConverter」でその配列を手動で作成しようとしましたか? –

+0

手作業でももちろん動作します私は何か一般的なものが必要です – tulipe

+1

それに反射マジックを振りかけると(['GetFields'](https://stackoverflow.com/questions/8067493/if-getfields-doesnt-guarantee-order- how-does-layoutkind-sequential-work))それは非常に一般的です。 –

答えて

0

あなたは、アレイを形成することにしたい正確にどのように定義するには明示的なレイアウトを使用することができます。

[StructLayout(LayoutKind.Explicit)] 
    public struct MyStruct 
    { 
     [FieldOffset(0)] 
     public ushort a; 
     [FieldOffset(2)] 
     public uint b; 
     [FieldOffset(6)] 
     public uint c; 
     [FieldOffset(10)] 
     public ushort d; 
    } 

しかし、もちろんそのすべてではない一般的な

+0

これはなぜ非常に簡潔に問題を解決し、期待される出力を得ますか –

+0

@MarcGravell私の考え方... –

+1

パックに関して、OPは、(プロジェクトのいくつかの要件のために)パック= 1を使用することはできないということを明示的に述べている。私は彼がFieldOffsetを使用することはできないと思います(もちろん、私はダウンボートしませんでした)。 – Evk

0

編集:私は質問を誤解している可能性があり、あなたのポイントがの特定のレイアウトを望むならば - Tim Rutter notes in their answerFieldOffsetのヒントを参照してください。


unsafe最も簡単なオプションであるかもしれない。しかし、それは非常に一般的ではありません

[StructLayout(LayoutKind.Sequential)] // see Tim's answer for explicit layout 
public struct MyStruct 
{ 
    public ushort a; 
    public uint b; 
    public uint c; 
    public ushort d; 
} 

:で

using System; 
using System.Runtime.InteropServices; 

static class P 
{ 
    private static unsafe void Main() 
    { 
     MyStruct orig = new MyStruct { a = 1, b = 2, c = 3, d = 4 }; 

     byte[] raw = new byte[sizeof(MyStruct)]; 
     // write the empty array to prove it is empty 
     Console.WriteLine(BitConverter.ToString(raw)); 


     // serialize 
     fixed (byte* p = raw) 
     { 
      var typed = (MyStruct*)p; 
      *typed = orig; 
     } 

     // write the serialized data 
     Console.WriteLine(BitConverter.ToString(raw)); 

     // deserialize 
     MyStruct clone; 
     fixed (byte* p = raw) 
     { 
      var typed = (MyStruct*)p; 
      clone = *typed; 
     } 

     Console.WriteLine($"a = {clone.a}, b = {clone.b}, c = {clone.c}, d = {clone.d}"); 
    } 
} 

。より一般的なコードが必要な場合:Unsafeタイプには、このような種類のユーティリティメソッドが多数あります。

+0

それでもパディングOPが回避しようとしています – Evk

+0

@Evkはい、それが私がTimの答えを指す上に編集を追加した理由です。しかし、私は 'マーシャル 'の使用を避ける方法を説明するためにこれをここに残すつもりです –

+0

パックを1に設定してみませんか? –

0

手動作業のビットを気にしない場合は、あなたがあなた自身のコンバータを書くことができ、例えば、それぞれ異なるデータ型を扱うには:

public static class StructSerializer 
{ 
    public static byte[] Serialize<T>(T data) where T : struct 
    { 
     List<byte> result = new List<byte>(); 
     Type type = data.GetType(); 
     IEnumerable<FieldInfo> orderedFields = type.GetFields().OrderBy(f => Marshal.OffsetOf(type, f.Name).ToInt32()); 

     foreach (FieldInfo fieldInfo in orderedFields) 
     { 
      object value = fieldInfo.GetValue(data); 
      MethodInfo conversion = typeof(BitConverter).GetMethod(nameof(BitConverter.GetBytes), new[]{fieldInfo.FieldType}); 
      if (conversion == null) continue; 
      byte[] converted = (byte[])conversion.Invoke(null, new []{value}); 
      result.AddRange(converted); 
     } 

     return result.ToArray(); 
    } 
} 

あなたは具体的には言及しなかったが、 ByteOrderを念頭に置いてください。 BitConverter.IsLittleEndianでバイトオーダを確認できます。

+0

私はCLIから、特にMetadataTokenを注文するのが賢明であることを保証していないことに気付いています。特に、ビルド間で作業を続けたい場合は –

+0

@MarcGravell Unfortunatly、yes。私はこの情報を[この回答](https://stackoverflow.com/a/8067702/3214843)から取りましたが、多分もっとよく動作するリファレンスソースで何かを見つけることができます。 –

+0

ああ、ぎこちない。完全に文書化されていない(または少なくとも:githubを介して非公式に文書化されている)、いくつかのケース(具体的には 'LayoutKind.Sequential')でしか動作することは知られているが、実際にはうまくいくように見えるが、 –

0

あなたの貴重な回答/コメントをありがとう!私は、手動で

 static byte[] getBytes<T>(T str) where T : struct 
    { 

     int size = Marshal.SizeOf(typeof(T)); 
     int Pack = str.GetType().StructLayoutAttribute.Pack; 
     byte[] arr = new byte[size]; 
     IntPtr ptr = Marshal.AllocHGlobal(size); 
     Marshal.StructureToPtr(str, ptr, true); 
     Marshal.Copy(ptr, arr, 0, size); 
     Marshal.FreeHGlobal(ptr); 
     arr = RemovePadding<T>(arr.ToList(), str, str.GetType().StructLayoutAttribute.Pack); 
     return arr; 
    } 
    static byte[] RemovePadding<T>(List<byte> buffer, T str, int Pack) 
    { 
     int largestsize = 0; 
     int index = 0; 
     int RowOfMemory = 0; 

     //Get all fields 
     var fields = str.GetType().GetFields(); 

     //After MSDN 
     //The alignment of the type is the size of its largest element (1, 2, 4, 8, etc., bytes) or the specified packing size, whichever is smaller. 
     foreach (var item in fields) 
     { 
      if (Marshal.SizeOf(item.FieldType) > largestsize) 
       largestsize = Marshal.SizeOf(item.FieldType); 
     } 
     if (largestsize < Pack) Pack = largestsize; 

     //Find and remove padding from all memory rows 
     foreach (var item in fields) 
     { 
      int size = Marshal.SizeOf(item.FieldType); 
      if (RowOfMemory != 0 && (RowOfMemory + size) > Pack) 
      { 
       int paddingsize = Math.Abs(Pack - RowOfMemory); 
       buffer.RemoveRange(index, paddingsize); 
       RowOfMemory = size % Pack; 
      } 
      else if ((RowOfMemory + size) < Pack) 
      { 
       RowOfMemory += size; 
      } 
      else if ((RowOfMemory + size) == Pack) 
      { 
       RowOfMemory = 0; 
      } 
      index += size; 
     } 
     if (RowOfMemory != 0) 
     { 
      int paddingsize = Math.Abs(Pack - RowOfMemory); 
      buffer.RemoveRange(index, paddingsize); 
     } 
     return buffer.ToArray(); 
    } 

は、私は正しい答えとしてこれをカウントされませんパディングを削除します誰かがよりよい解決策を見つけることができる回避策を発見しました。

関連する問題