4

ワイヤ形式のパケットを表す構造があります。この構造体には、他の構造体の配列があります。ほとんどのケースでこれを非常にうまく処理する汎用コードがありますが、この構造体の配列のケースはループのマーシャラーをスローしています。

配列を持つ構造体へのポインターを取得できないため、アンセーフ コードは使用できません (ぐーん!)。

このコードプロジェクトの記事から、次のような C++/CLI を含む非常に優れた一般的なアプローチがあることがわかります...

public ref class Reader abstract sealed
    {
    public:
        generic <typename T> where T : value class
        static T Read(array<System::Byte>^ data)
        {
            T value;

            pin_ptr<System::Byte> src = &data[0];
            pin_ptr<T> dst = &value;

            memcpy((void*)dst, (void*)src,
                /*System::Runtime::InteropServices::Marshal::SizeOf(T::typeid)*/
                sizeof(T));

            return value;
        }
    };

今、構造があれば -> バイト配列 / ライターのバージョンが設定されます! 前もって感謝します!

4

5 に答える 5

2

構造体のバイト パッキングを制御していない場合、memcpy を使用してバイト配列を構造体にコピーすることは非常に危険です。一度に 1 フィールドずつ構造体をマーシャリングおよびアンマーシャリングする方が安全です。もちろん、提供したサンプル コードの一般的な機能は失われます。

ただし、実際の質問に答えるには(この疑似コードを検討してください):

public ref class Writer abstract sealed
    {
    public:
        generic <typename T> where T : value class
        static System::Byte[] Write(T value)
        {
            System::Byte buffer[] = new System::Byte[sizeof(T)]; // this syntax is probably wrong.
            pin_ptr<System::Byte> dst = &buffer[0];
            pin_ptr<T> src = &value;

            memcpy((void*)dst, (void*)src,
                /*System::Runtime::InteropServices::Marshal::SizeOf(T::typeid)*/
                sizeof(T));

            return buffer;
        }
    };
于 2008-12-01T05:16:31.227 に答える
2

これはおそらく正しい方法ではありません。CLR では、パディングの追加、アイテムの並べ替え、メモリへの格納方法の変更が許可されています。

これを行う場合は[System.Runtime.InteropServices.StructLayout]、構造体に特定のメモリ レイアウトを強制する属性を必ず追加してください。一般に、.NET 型のメモリ レイアウトをいじらないことをお勧めします。

于 2008-12-01T07:19:00.713 に答える
1

実際には、安全でないコードを作成してこれを行うことができます。ディスクからの構造体の読み取りに関する私の投稿を参照してください: Reading arrays from files in C# without extra copy.

于 2009-02-14T19:06:27.613 に答える
0

何か不足していますか?同じサイズの新しい配列を作成し、ループ内で各要素を個別に初期化してみませんか?

バイト データの配列を使用することは、1 つのプラットフォームのみをターゲットにしている場合を除き、非常に危険です。

あなたの質問について私がよく理解していないのは、クラスのメンバーとして配列を持つことが問題を引き起こしている理由です。クラスが .NET 言語からのものである場合、問題はないはずです。それ以外の場合は、安全でないコードでポインターを取得し、ポイントされている要素を 1 つずつ (安全でないコードで) 調べて追加することで、新しい配列を初期化できるはずです。それらに。

于 2009-02-03T13:57:44.007 に答える
0

構造を変更しないことは確かに適切なアドバイスです。パッキング、レイアウト、および文字エンコーディングを指定するために、自由な量の StructLayout 属性を使用します。すべてがうまく流れます。

私の問題は、パフォーマンスが高く、できれば汎用的なソリューションが必要だということです。これはサーバーアプリケーションであり、エレガンスのための汎用であるため、パフォーマンス。codeproject リンクを見ると、StructureToPtr メソッドと PtrToStructure メソッドの実行速度が、単純な安全でないポインター キャストよりも 20 倍程度遅いことがわかります。これは、安全でないコードが有利に働く領域の 1 つです。C# では、プリミティブへのポインターしか持てません (ジェネリックではなく、ジェネリックへのポインターを取得できません)。そのため、CLI を使用します。

疑似コードの悲しみに感謝します。仕事が完了するかどうかを確認し、報告します。

于 2008-12-01T21:14:10.277 に答える