3

私の質問は、参照型のシリアル化されたサイズ(バイト単位)を判別できるかどうかです。

状況は次のとおりです。

BinaryFormatterクラスを使用して、基本的な.NETタイプをシリアル化します。たとえば、次のようになります。

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

各アイテムをbyte[]にシリアル化し、そのセグメントを既存のbyte []の最後に追加し、さらに各セグメントの最後にキャリッジリターンを追加してオブジェクトを区切ります。

デシリアライズするには、次のように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のシリアル化が一般的なList <>の場合、オブジェクトを区切る特定の方法はありますか?

4

4 に答える 4

4

バイナリシリアル化データの区切り文字としてバイトを使用するのはひどい考えです。13は完全に有効な値であり、「区切り文字」だけでなく、シリアル化されたデータの一部にすることができます。

代わりに、各ブロックの前にバイト単位のサイズを付けて、ブロック単位で読み取ります。

于 2012-04-13T21:22:34.813 に答える
4

シリアル化された長さを事前に決定するためのひどく良い方法はありません。BinaryFormatterプロトコルの仕様は、次の場所にあります: http ://msdn.microsoft.com/en-us/library/cc236844(v=prot.10).aspx

私はあなたの目的のためにそれを読む手間を省きます:

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

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

最後に、余分なメモリコピーは、現在の実装における非効率性の主な原因ではないことをお伝えできます。BinaryFormatterのリフレクションの使用と、シリアル化された出力のフィールド名をエンコードするという事実から、はるかに非効率になっています。

効率が最優先される場合は、構造のコンテンツを「プレーンな古いデータ」形式でエンコードするカスタムコードを作成することをお勧めします。次に、書き込まれる量と方法を制御できます。

于 2012-04-13T21:32:55.957 に答える
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はアクセスを許可します。

于 2012-04-13T21:23:37.290 に答える
1

https://bytes.com/topic/c-sharp/answers/238927-object-size-memoryからこのコードを使用して、まったくシリアル化されない原因を見つけることができました

var m = new System.IO.MemoryStream();
var b = new
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
b.Serialize(m, Obj);
var size = Convert.ToDouble(m.Length);
于 2018-06-27T16:14:00.607 に答える