2012-01-27 23 views
5

Tôi có một số mã nhằm có được một cấu trúc từ một mảng byte:Tại sao tôi không thể sử dụng Marshal.Copy() để cập nhật cấu trúc?

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; 
    } 

Tuy nhiên, cấu trúc d không bao giờ thay đổi, và luôn luôn trả về giá trị mặc định của nó.

Tôi đã tra cứu cách 'đúng' để thực hiện việc này và đang sử dụng thay vào đó, nhưng tôi vẫn tò mò, vì tôi không thể thấy lý do tại sao điều này không hoạt động.

Nó đơn giản như có thể là: cấp phát một số bộ nhớ, d, có được một con trỏ đến nó, sao chép một số byte vào bộ nhớ chỉ bằng cách này, trở về. Không chỉ vậy, nhưng khi tôi sử dụng mã tương tự nhưng với d là một mảng của T, nó hoạt động tốt. Trừ khi sh.AddrOfPinnedObject() không thực sự trỏ đến d, nhưng sau đó điểm của nó là gì?

Bất cứ ai có thể cho tôi biết lý do trên không hoạt động?

+0

Chỉ cần tò mò là cách 'chính xác' là gì? – Dmitry

+0

@ Dmitry, Xin chào, cách chính xác là sử dụng PtrToStructure() chuyển một con trỏ tới bộ nhớ không được quản lý có chứa nội dung của cấu trúc như được mô tả chi tiết tại đây: http://msdn.microsoft.com/en-us/library/4ca6d5z7.aspx Sử dụng phản xạ nó có thể được nhìn thấy PtrToStructure() instantiates một đối tượng mới và populates nó, mặc dù nó như thế nào tôi không chắc chắn vì những chi tiết tôi tin là trong CLR mà tôi không thể nhìn thấy (http://stackoverflow.com/questions/11788625/pinvoke-win32-function-for-marshal-ptrtostructure-in-silverlight-5) – sebf

Trả lời

4

Cảnh báo chi tiết triển khai cảnh báo, điều này có thể không đúng trong các phiên bản sau của .Net.

structs là các loại giá trị và (thường) được lưu trữ trên ngăn xếp (*), không phải trên vùng heap. Địa chỉ của một cấu trúc là vô nghĩa vì chúng được truyền theo giá trị, không phải bằng tham chiếu. Mảng cấu trúc là một kiểu tham chiếu, đó là một con trỏ tới bộ nhớ trên heap, do đó địa chỉ trong bộ nhớ là hoàn toàn hợp lệ.

Mấu chốt của AddrOfPinnedObject là để có được địa chỉ của một đối tượng thats nhớ được gắn, không phải là một struct.

Ngoài ra, Eric Lippert đã viết a series of very good blog posts về chủ đề của loại tham chiếu và loại giá trị.

(*) Trừ:

1 Họ là những lĩnh vực trên một lớp
2 Chúng được đóng hộp
3 Họ là những "biến bắt"
4 Họ đang ở trong một khối iterator

(nb điểm 3 và 4 là hệ quả của điểm 1)

+1

Cần lưu ý rằng 'structs' có thể được lưu trữ trên' heap', điều này sẽ xảy ra khi chúng là thành viên của một 'lớp' – gdoron

+0

@gdoron Một điểm tốt, câu trả lời được cập nhật. –

+0

Điều này làm cho cảm giác hoàn hảo, cảm ơn bạn! – sebf

1

Đây là ví dụ:

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); 
     } 
    } 
} 

Nhưng tôi sẽ sử dụng bố cục rõ ràng tại đây vì 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); 

Đó là nơi mà vấn đề của bạn bắt đầu. Cấu trúc là loại giá trị , GCHandle.Alloc() chỉ có thể phân bổ các chốt cho các loại tham chiếu . Loại có đối tượng được phân bổ trên đống rác thu thập được. Và loại làm cho pinning hợp lý. Trình biên dịch C# đang được một chút quá hữu ích ở đây, nó tự động phát ra một chuyển đổi quyền anh để hộp giá trị và làm cho công việc tuyên bố. Đó là bình thường rất tốt đẹp và tạo ra những ảo tưởng rằng các loại giá trị có nguồn gốc từ System.Object. Cách gõ giống như một con vịt.

Vấn đề là, Marshal.Copy() sẽ cập nhật bản sao đóng hộp giá trị. Không phải biến của bạn. Vì vậy, bạn không thấy nó thay đổi.

Cập nhật trực tiếp giá trị cấu trúc chỉ có thể với Marshal.PtrToStructure(). Nó chứa các thông số cần thiết để chuyển đổi bố cục đã xuất bản của cấu trúc (thuộc tính StructLayout) thành bố cục bên trong. Đó là không giống nhau và nếu không không thể khám phá.

+0

+1 Cảm ơn bạn đã giải thích về đấm bốc tự động của d; Tôi có thể thấy đầy đủ cách mã của tôi (không!) Hoạt động ngay bây giờ. – sebf

+2

Các loại giá trị * được * lấy từ System.Object. Nó không phải là ảo giác! –

+0

Chắc chắn. Nếu chỉ trừu tượng sẽ không bao giờ bị rò rỉ. –

Các vấn đề liên quan