5

C# では、理想的には追加のバッファーなしで、ストリームに T[] を書き込む必要があります。T[] (T はオブジェクトのない構造体) を void* に変換し、メモリ内で修正する動的コードがあり、うまく機能します。ストリームがファイルだったときは、ネイティブ Windows API を使用して void * を直接渡すことができましたが、今は byte[] を受け取る汎用 Stream オブジェクトに書き込む必要があります。

質問:実際にはヒープ割り当てを持たず、既存の (そして固定された) ヒープ位置を指すダミー配列オブジェクトを作成するハック方法を誰か提案できますか?

これは私が必要とする疑似コードです:

void Write(Stream stream, T[] buffer)
{
    fixed( void* ptr = &buffer )    // done with dynamic code generation
    {
        int typeSize = sizeof(T);   // done as well

        byte[] dummy = (byte[]) ptr;   // <-- how do I create this fake array?

        stream.Write( dummy, 0, buffer.Length*typeSize );
    }
}  

更新:この記事fixed(void* ptr=&buffer)で詳細な 方法を説明しました。私はいつでもバイト[]を作成し、それをメモリ内で修正し、あるポインターから別のポインターへの安全でないバイトコピーを実行し、その配列をストリームに送信することができましたが、不要な余分な割り当てとコピーを避けることを望んでいました.

不可能? さらに考えてみると、byte[] には、配列の次元と要素の型を含むいくつかのメタデータがヒープに含まれています。T[] への参照 (ポインター) を byte[] として渡すだけでは、ブロックのメタデータが T[] のままであるため、機能しない場合があります。また、メタ データの構造が同一であっても、T[] の長さは byte[] よりもはるかに短くなるため、マネージ コードによるその後の byte[] へのアクセスは、正しくない結果を生成します。

機能要求 @ Microsoft Connectこの要求に 投票してください。MS が耳を傾けてくれることを願っています。

4

4 に答える 4

3

この種のコードは、一般的な方法では機能しません。これは、Tのメモリレイアウトが予測可能で一貫しているという厳しい仮定に依存しています。これは、Tが単純な値型である場合にのみ当てはまります。エンディアンをしばらく無視します。Tが参照型の場合、あなたは水中で死んでいます。逆シリアル化できない追跡ハンドルをコピーすることになります。Tに構造体制約を与える必要があります。

しかし、それだけでは十分ではありません。構造型もコピーできません。参照型フィールドがない場合でも、制約することはできません。内部レイアウトは、JITコンパイラによって決定されます。自由にフィールドを交換し、フィールドが適切に配置され、構造体の値が最小のストレージサイズをとるフィールドを選択します。シリアル化する値は、まったく同じCPUアーキテクチャとJITコンパイラバージョンで実行されるプログラムによってのみ正しく読み取ることができます。

フレームワークには、あなたがしていることを実行するクラスがすでにたくさんあります。最も近いのは.NET4.0MemoryMappedViewAccessorクラスです。同じジョブを実行して、メモリマップトファイルでrawバイトを使用できるようにする必要があります。そこにある主力製品はSystem.Runtime.InteropServices.SafeBufferクラスであり、Reflectorを参照してください。残念ながら、クラスをコピーするだけではなく、CLRに依存して変換を行います。繰り返しになりますが、利用可能になるまであと1週間です。

于 2010-04-03T14:05:10.533 に答える
0

この記事を見てくださいC#/VB.NETのインラインMSILとGenericPointersは、夢のコードを取得するための最良の方法です:)

于 2012-09-26T21:12:36.687 に答える
0

stream.Write はポインターを受け取ることができないため、メモリのコピーを避けることはできず、速度が低下します。BinaryReader と BinaryWriter を使用してオブジェクトをシリアル化することを検討することをお勧めしますが、ここに必要なことができるコードを示します。T のすべてのメンバーも構造体でなければならないことに注意してください。

unsafe static void Write<T>(Stream stream, T[] buffer) where T : struct
{
    System.Runtime.InteropServices.GCHandle handle = System.Runtime.InteropServices.GCHandle.Alloc(buffer, System.Runtime.InteropServices.GCHandleType.Pinned);
    IntPtr address = handle.AddrOfPinnedObject();
    int byteCount = System.Runtime.InteropServices.Marshal.SizeOf(typeof(T)) * buffer.Length;
    byte* ptr = (byte*)address.ToPointer();
    byte* endPtr = ptr + byteCount;
    while (ptr != endPtr)
    {
        stream.WriteByte(*ptr++);
    }
    handle.Free();
}
于 2010-04-03T03:12:36.973 に答える
0

関連する質問に対する私の回答を確認してください: float[] を byte[] に変換する最速の方法は何ですか?

その中で、メモリの割り当てとコピーを行わずに、float の配列をバイトの配列に一時的に変換します。これを行うために、メモリ操作を使用して CLR のメタデータを変更しました。

残念ながら、このソリューションはジェネリックには適していません。ただし、このハックをコード生成手法と組み合わせて問題を解決できます

于 2010-10-28T04:42:06.967 に答える