5

最近、バイト配列からデータ型を読み取るジェネリック メソッドを作成する必要がある状況に遭遇しました。

次のクラスを作成しました。


public class DataStream
{
    public int Offset { get; set; }

    public byte[] Data { get; set; }

    public T Read<T>() where T : struct
    {
        unsafe
        {
            int dataLen = Marshal.SizeOf( typeof( T ) );
            IntPtr dataBlock = Marshal.AllocHGlobal( dataLen );


            Marshal.Copy( Data, Offset, dataBlock, dataLen );


            T type = *( ( T* )dataBlock.ToPointer() );

            Marshal.FreeHGlobal( dataBlock );

            Offset += dataLen;

            return type;
        }
    }
}

さて、割り当て解除の問題は別として、このコードは次のメッセージでコンパイルされません。

マネージド型 ('T') のアドレスを取得したり、サイズを取得したり、ポインタを宣言したりすることはできません

where T : structメソッドの制約に基づいて上記の操作を実行できるはずなので、これは奇妙に思えます。

このコードがひどく間違っている場合、一連のバイトを取得して ' T' 型にキャストする簡単な方法はありますか?

ありがとう!

4

5 に答える 5

9

ポインター操作でこれを行う代わりに、コードを切り替えてMashal.PtrToStructureを使用する必要があります。この方法は、このシナリオ用に特別に設計されています。

于 2009-09-21T17:05:30.643 に答える
8

答えはすでに与えられているので、元のコードが機能しなかった理由を説明しましょう。

メソッドの where T : struct 制約に基づいて上記の操作を実行できるはずなので、これは奇妙に思えます。

あまり。アンマネージ型への生のポインタを持つことができます。これは、C# 言語仕様 (18.2) で次のように定義されています。

参照 (参照型の値) とは異なり、ポインターはガベージ コレクターによって追跡されません。ガベージ コレクターは、ポインターとポインターが指すデータを認識しません。このため、ポインターが参照または参照を含む構造体を指すことは許可されておらず、ポインターの参照先の型はunmanaged-typeでなければなりません。アンマネージ型は、参照型ではなく、入れ子のどのレベルでも参照型フィールドを含まない任意の型です。つまり、管理されていない型は次のいずれかです。

  • sbyte、、、、、、、、、、、、、、または。byte_ short_ ushort_ int_ uint_ long_ ulong_ char_ float_ double_decimalbool
  • 任意の列挙型
  • 任意のポインター型
  • アンマネージ型のみのフィールドを含むユーザー定義の構造体型。

したがって、かなりの数の制限があり、ジェネリックメソッドのT:struct場合、特定のインスタンス化に対してそれらに準拠する場合と準拠しない場合があるため、construct likeT*は違法です。アンマネージ型をカバーする特別なジェネリック型パラメーター制約があると便利ですが、現状では、CLR にはありません。

于 2009-09-21T17:16:45.193 に答える
2

ある時点で、私はそれを正確に行う方法を説明するこの記事を書きましたが、Marshal.PtrToStructureよりも何倍も高速です。コードサンプルは、動的コード生成を使用して、ジェネリック型Tをビットストリームとの間でコピーします。

于 2009-09-25T16:13:54.647 に答える
1

仮定:

  • シーケンシャルまたは明示的な構造(そうでなければ非常に悪い考え)
  • 正しいデータサイズ(事前に確認してスローする必要があります)

unsafe TStruct BytesToStructure<TStruct>(byte[] data) where TStruct : struct
{
    fixed (byte* dataPtr = data)
        return (TStruct)Marshal.PtrToStructure(new IntPtr(dataPtr), typeof(TStruct));
}

unsafe byte[] StructureToBytes<TStruct>(TStruct st) where TStruct : struct
{
    var bytes = new byte[Marshal.SizeOf(st)];
    fixed (byte* ptr = bytes) Marshal.StructureToPtr(st, new IntPtr(ptr), true);
    return bytes;
}
于 2012-10-24T09:35:38.527 に答える
0

私は同じことをするためにこれをしばらく前に書きました: http://www.codeproject.com/Articles/33713/Generic-BinaryReader-and-BinaryWriter-Extensions

ただし、C++/CLI プロジェクトを Visual Studio ソリューションに追加する必要があります。

于 2012-06-05T03:10:47.993 に答える