2012-01-27 11 views
5

は、私はバイト配列から構造体を取得することを目的といくつかのコードがあります。Marshal.Copy()を使用して構造体を更新できないのはなぜですか?

public static T GetValue<T>(byte[] data, int start) where T : struct 
    { 
     T d = default(T); 
     int elementsize = Marshal.SizeOf(typeof(T)); 

     GCHandle sh = GCHandle.Alloc(d, GCHandleType.Pinned); 
     Marshal.Copy(data, start, sh.AddrOfPinnedObject(), elementsize); 
     sh.Free(); 

     return d; 
    } 

をしかし、構造dは変更されることはありませんし、常にそのデフォルト値を返します。

私はこれを行うには正しい方法を探していますが、その代わりにそれを使用していますが、それでもうまくいかない理由はわかりません。

そのように簡単です:いくつかのメモリを割り当て、d、ポインタを取得し、これによってポイントされるメモリにいくつかのバイトをコピーします。 それだけでなく、でも同様のコードを使用していますが、dがTの配列であるとうまくいきます。 sh.AddrOfPinnedObject()が実際にdを指していない限り、そのポイントは何ですか?

誰でも私が上記の理由で動作しない理由を教えてもらえますか?

+0

「正しい」方法は何だったのですか? – Dmitry

+0

@Dmitry、こんにちは、PtrToStructure()を使用して、構造体の内容を含むアンマネージメモリへのポインタを渡す方法があります。http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx リフレクターを使用すると、PtrToStructure()は新しいオブジェクトをインスタンス化し、それを挿入しますが、それは私が見ることができないCLRにあると考えられるため、確信が持てません(http://stackoverflow.com/questions/11788625/pinvoke-win32-function-for-marshal-ptrtostructure-in-silverlight-5) – sebf

答えて

4

警告実装の詳細警告、これは、今後のバージョンの.Netでは当てはまりません。

structsは値の型であり、(一般的に)ヒープ上ではなくスタック(*)に格納されます。構造体のアドレスは、参照ではなく値渡しであるため、無意味です。構造体の配列は参照型です。つまり、ヒープ上のメモリへのポインタです。したがって、メモリ内のアドレスは完全に有効です。

AddrOfPinnedObjectのポイントが固定されているメモリのthats オブジェクトのアドレスを取得することです、ない構造体

さらに、Eric Lippertは参照型と値型の対象としてa series of very good blog postsと書いています。

(*)がない限り:彼らは、彼らが反復子ブロック

(NBにある "撮影変数"
4である
3四角で囲まれているクラスのフィールド
2である

1点3及び4は、ポイント1の帰結である)

+1

''ヒープ 'に 'structs'を格納できることに注意してください。 – gdoron

+0

@gdoron良い点、答えが更新されました。 –

+0

これは完璧な意味があります、ありがとう! – sebf

1

ここで実施例は次のとおり

public static T GetValue<T>(byte[] data, int start) where T : struct 
{ 
    int elementsize = Marshal.SizeOf(typeof(T)); 

    IntPtr ptr = IntPtr.Zero; 

    try 
    { 
     ptr = Marshal.AllocHGlobal(elementsize); 

     Marshal.Copy(data, start, ptr, elementsize); 
     return (T)Marshal.PtrToStructure(ptr, typeof(T)); 
    } 
    finally 
    { 
     if (ptr != IntPtr.Zero) 
     { 
      Marshal.FreeHGlobal(ptr); 
     } 
    } 
} 

struct alignmentのため、ここでは明示的なレイアウトを使用します。あなたの問題はスタート地点だ

[StructLayout(LayoutKind.Explicit, Size = 3)] 
public struct TestStruct 
{ 
    [FieldOffset(0)] 
    public byte z; 

    [FieldOffset(1)] 
    public short y; 
} 
8
GCHandle sh = GCHandle.Alloc(d, GCHandleType.Pinned); 

。構造体は値タイプであり、GCHandle.Alloc()は参照タイプのハンドルのみを割り当てることができます。オブジェクトがガベージコレクションされたヒープに割り当てられている種類。そしてピンチを賢明にする種類。 C#コンパイラはちょっと役に立っていますが、自動的に値をボックスに変換してステートメントを動作させるボクシング変換を行います。これは通常非常にいいですし、値の型がSystem.Objectから派生しているという錯覚を作り出します。クックのようなアヒルのタイピング。

問題は、Marshal.Copy()はボックス化コピーの値を更新します。 あなたの変数。だからあなたはそれが変わるのを見ない。

構造体の値を直接更新することは、Marshal.PtrToStructure()でのみ可能です。構造体の公開レイアウト(StructLayout属性)を内部レイアウトに変換するために必要なスマートが含まれています。それは同じではなく、そうでなければ発見できないものです。

+0

+1 dの自動ボクシングを説明していただきありがとうございます。私は自分のコードがどのように(今は)働いているか完全に見ることができます。 – sebf

+2

値の型はSystem.Objectから派生しています。それは錯覚ではありません! –

+0

抽象だけが漏れることはありません。 –

関連する問題