2012-04-13 14 views
3

私の質問は、参照型のシリアル化されたサイズ(バイト単位)を決定できるかどうかです。.NETタイプと管理されていないメモリ効率のシリアル化されたサイズの決定

相続人の状況:

私はすなわち、たとえば、基本的な.NET型をシリアル化するためにBinaryFormatterクラスを使用しています:

[Serializable] 
public class Foo 
{ 
    public string Foo1 { get; set; } 
    public string Foo2 { get; set; } 
} 

私がいることを追加した後、[]バイトに各項目をシリアル化していますセグメントを既存のバイト[]の終わりまで追加し、オブジェクトの境界を定めるために各セグメントの最後にキャリッジリターンを追加します。私は(Marshal.ReadByteを使うデシリアライズするために

)を次のように

List<byte> buffer = new List<byte>(); 

for (int i = 0; i < MapSize; i++) 
{ 
    byte b = Marshal.ReadByte(readPtr , i); 

    if (b != delim) // read until encounter a carriage return 
     buffer.Add(b); 
    else 
     break; 
} 

readPtr = readPtr + buffer.Count + 1; // incrementing the pointer for the next object 

return buffer.ToArray(); 

私はMarshal.Copy()を使用すると、より効率的であると信じていますが、私はシリアル化されたバイトのセグメントの長さを知っている必要がありますあらかじめ。シリアル化されている型か、より効率的な方法を使って確実にこれを計算する方法はありますか?

また、キャリッジリターンの使用は最終的には信頼できません。だから私はBinaryFormatterをカスタマイズするか、または他の標準化されたベストプラクティスを使用して、オブジェクトを区切る標準的な方法があるかどうか疑問に思っています。たとえば、BinaryFormatterでシリアル化がGeneric List <>のように指定されている場合、そのオブジェクトを区切る具体的な方法がありますか?

+1

これは危険です。シリアライズされたデータ自体に値13のバイトが表示されたらどうなりますか? IMO **固定長(通常は4または8バイト)のネットワークバイトオーダーのエンコーディング、またはエキゾチックな感じの場合は "varint"エンコーディングのようなものを使用して**プレフィックス**を付ける必要があります –

+0

@MarcGravell、ありがとうございます。キャリッジは一時的な解決策として返されます。長さの接頭辞の考えは良いです、私はそれを使用すると思います。 –

+0

実際には、長さの区切りをまったく行う必要はありません。 BinaryFormatterは、完全なエンクロージャをデシリアライズしたときを認識します。 –

答えて

3

あらかじめシリアライズされた長さを判断する方法はあまりありません。 BinaryFormatterプロトコルの仕様は、ここに提供されています: http://msdn.microsoft.com/en-us/library/cc236844(v=prot.10).aspx

私はあなたの目的のためにそれを読んでの手間を節約できます:

  1. 拡張可能なフォーマットであることを内蔵しています。これにより、フィールドを後で追加しても、以前の実装との互換性を維持できます。あなたの目的のために、これは、シリアル化されたフォームの長さが固定されていないことを意味します。
  2. 非常に壊れやすいです。バイナリ形式は、実際にはその中のフィールドの名前をエンコードします。フィールドの名前を変更すると、シリアル化されたフォームの長さが変更されます。
  3. バイナリ形式には、実際にはシリアル化されたエンコードとオブジェクトデータの多対1の関係が含まれます。同じオブジェクトは、さまざまな方法でエンコードされる可能性があります。出力のバイト数はさまざまです(このように書かれた理由はわかりません)。

簡単にやりたいことがある場合は、すべてのオブジェクトを含む配列を作成し、その1つの配列をシリアル化してください。これはあなたの問題のほとんどを解決します。異なるオブジェクトを区切るすべての問題は、BinaryFormatterによって処理されます。過度のメモリコピーは必要ありません。 BinaryFormatterは呼び出しごとに一度フィールド名を指定するだけで済むため、最終出力はよりコンパクトになります。

最後に、余分なメモリコピーが現在の実装の非効率性の主な原因ではないことがわかります。 BinaryFormatterのリフレクションの使用と、シリアライズされた出力のフィールド名をエンコードするという事実により、はるかに非効率的になります。

効率が重要な場合は、構造の内容を「普通の古いデータ」形式でエンコードするカスタムコードを書くことをお勧めします。それで、あなたはどれくらいの文章が書かれ、どのように書かれているかを制御します

+0

ありがとうございます - 私はホイールを再開発する時間を節約するために得ることができる既存の「普通の古いデータ」フォーマッタを知っていますか?私は最終的に私自身のロールを張ると思いますが、当分の間、私は概念実証をやっています。 –

+0

私は「あなたが簡単な方法を望むなら...」という段落の1つを書き直しました。それはあなたにもっと良いやり方を与えるはずです。 PODフォーマッタに関しては、それらは一回限りである傾向があります。オプションとしてGoogleのプロトコルバッファをチェックしてください。ただし、独自の長所と短所があります。 –

4

バイナリシリアライズされたデータの区切り文字としてバイトを使用することは賢明です.13は完全に有効な値で、デリミタだけでなくシリアル化されたデータの一部でもかまいません。

各ブロックのサイズをバイト単位でプレフィックスし、ブロック単位で読み込みます。

+1

ちょっと...私の考え。 –

2

Marshal.SizeOfを使用すると、構造体のネイティブサイズを取得できます。これは構造体でのみ機能し、StructLayout属性を設定することをお勧めします。

それはまだ重要な驚くべきことであるので、私はコメントからいくつかの情報を引き出します:

CLRが固定構造体やクラスのネイティブレイアウトを作成するためのメタデータ機能を持っています。 C#では、これは構造体に対してのみ可能です。しかし、クラスもそのように使うことができます。

SequentialLayoutを指定した場合、マネージ型をバイトにビットブリットすることができます。 http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.structlayoutattribute.aspxこの機能はよく知られていませんが、存在し、指定され、サポートされています。引用: "クラスレイアウトの属性(AutoLayout、SequentialLayout、ExplicitLayout)は、クラスインスタンスのフィールドがのメモリに配置される方法を定義します。"

System.Reflection.TypeAttributes列挙型を見てください。他のCLRレベルの属性も定義します。 C#はそれらにアクセスできませんが、ilasm.exeはアクセスします。

+0

参照型は「ネイティブサイズ」(一度シリアライズされたとにかく)を持つことはできません。これは私には分かりません。 –

+0

CLRには、構造体またはクラスのネイティブレイアウトを固定するためのメタデータ機能があります。 C#では、これは構造体に対してのみ可能だと思います。しかし、クラスもそのように使うことができます。 – usr

+0

オブジェクトのメモリ内サイズは、BinaryFormatterでシリアル化されたときのサイズとは関係ありません。 –

関連する問題