2012-11-08 29 views
8

Tôi đã thực hiện một chút nghiên cứu công bằng, nhưng hiện tại tôi đang gặp phải lý do tại sao tôi vẫn gặp phải lỗi này. Tôi có một cấu trúc với các thuộc tính sau:Không thể lấy địa chỉ, lấy kích thước hoặc khai báo con trỏ đến loại được quản lý

struct Account 
{ 
    //private attributes 
    private double mBalance; 
    private int mAccountNumber; 
    private string mName; 
    private string mDateCreated; 
} 

và đang cố gắng để làm như sau:

class BankManager 
{ 
    //private attributes 
    private unsafe Account *mAccounts; 
    private unsafe bool *mAccountsAvailable; 
    private int mNumberAccounts; 
} 

Ngay cả sau khi chuyển class Account của tôi đến một cấu trúc, sử dụng "không an toàn" cho các thuộc tính trong lớp học BankManager, và nói với trình biên dịch nó có thể sử dụng mã không an toàn (trong thuộc tính -> Xây dựng), tôi vẫn gặp lỗi này tại

*mAccounts 

Bất kỳ ý tưởng nào về lý do tại sao? Tôi khá chắc chắn rằng tất cả các loại tôi đang sử dụng trong cấu trúc là hợp pháp để có con trỏ đến trong C#. Cảm ơn trước!

+1

Tại sao bạn muốn sử dụng con trỏ? Có vẻ như 'BankManager' sẽ có một' Bộ sưu tập' của 'Tài khoản'. – Xint0

+0

Điều này có thể giúp: http://stackoverflow.com/questions/2559384/cannot-take-the-address-of-get-the-size-of-or-declare-a-pointer-to-a-managed-t – sellmeadog

Trả lời

20

Các chuỗi trong lớp Tài khoản gây ra sự cố này. Để hiểu tại sao, bạn cần phải hiểu cách thức hoạt động của bộ thu gom rác. Nó phát hiện ra rác bằng cách theo dõi các tham chiếu đến các đối tượng. MName và mDateCreated là các tham chiếu như vậy. MBalance và mAccountNumber là không, các trường đó là các loại giá trị. Và, quan trọng nhất, trường BankManager.mAccounts không phải là, nó là một con trỏ.

Vì vậy, trình biên dịch có thể biết trước rằng bộ thu gom rác sẽ không bao giờ có thể xem các tham chiếu chuỗi. Bởi vì cách duy nhất để làm như vậy là đi qua trường mAccount và nó không phải là một tham chiếu.

Cách chữa trị duy nhất cho điều này là hạn chế chính xác các loại giá trị. Cách duy nhất để làm điều đó cho chuỗi là phân bổ chúng trong không được quản lý bộ nhớ với, ví dụ, Marshal.StringToCoTaskMemUni() và lưu trữ IntPtr trong trường. Nó bây giờ là ngoài tầm với từ các nhà sưu tập rác và không thể nhận được di chuyển của nó. Bây giờ bạn sẽ có gánh nặng giải phóng chuỗi đó.

Rõ ràng là không thực tế và dễ bị rò rỉ, loại vấn đề rất phổ biến trong các chương trình C. Không chắc chắn lý do tại sao bạn đang theo đuổi điều này ở tất cả nhưng hãy ghi nhớ rằng một tham chiếu đến một đối tượng đã là một con trỏ đơn giản, do đó bạn không đạt được bất cứ điều gì bằng cách sử dụng con trỏ mình.

+0

Cảm ơn thông tin chi tiết! – SantasNotReal

0

Bạn sai về cấu trúc chứa các loại có thể có con trỏ, bởi vì string là loại được quản lý không thể có tham chiếu con trỏ.

1

Sử dụng private unsafe fixed char mName[126];
Chuỗi là loại được quản lý và do đó là mảng không cố định.

+0

Ok, tôi hiểu rằng các chuỗi được quản lý, nhưng ký tự được cho phép. Vì vậy, khi tôi làm như trên, tôi nhận được: Tên công khai công khai { nhận {return mName; } đặt {mName = value; } } với lỗi "Bạn không thể sử dụng bộ đệm kích thước cố định chứa trong các biểu thức không được cố định. Hãy thử sử dụng câu lệnh cố định". – SantasNotReal

3

Chuỗi là loại tham chiếu trong .NET và không phải là blittable cho cấu trúc con trỏ. Xem Blittable and Non-Blittable Types để biết danh sách các loại giá trị cho những gì bạn muốn làm.

Trừ khi bạn có yêu cầu kinh doanh đặc biệt, bạn nên gắn bó với bộ nhớ được quản lý để bảo trì và độ tỉnh táo chung.

+0

Câu trả lời này mang lại lợi ích cho tôi hơn là câu trả lời được chấp nhận vì nó mô tả vấn đề thiết kế lớn hơn. Vấn đề cụ thể của tôi liên quan đến một cấu trúc có chứa một thuộc tính mảng int [], mà không phải là blittable, và kết quả trong cùng một lỗi như OP. –

-1

Dữ liệu được quản lý không ở vị trí cố định vì trình thu thập sao chép có thể di chuyển mọi thứ xung quanh. Điều này cũng đúng với các loại giá trị được đóng hộp được quản lý. Các kiểu giá trị không được quản lý có thể chỉ sống trên ngăn xếp hoặc bên trong các đối tượng khác. Họ chỉ có vị trí cố định nếu họ đang ở trong ngăn xếp.

Để tạo cấu trúc phân bổ heap có vị trí cố định mà từ đó bạn có thể lấy con trỏ sẽ tiếp tục hợp lệ, bạn phải phân bổ nó trong bộ nhớ không được quản lý. Tuy nhiên, khi bạn cấp phát bộ nhớ trong bộ nhớ không được quản lý, bạn không thể đặt con trỏ được quản lý trong đó nữa (aka, bạn không thể sử dụng chuỗi), vì trình thu thập rác sẽ không biết về các con trỏ đó để nó không cập nhật chúng khi nó di chuyển các đối tượng được quản lý xung quanh trong quá trình nén.

Ví dụ, đây là một hợp lệ (mặc dù không nhất thiết phải tốt) điều cần làm:

[StructLayout(LayoutKind.Sequential, Pack=1)] 
public unsafe struct Account { 
    public int a; 
    public char* mName; 
} 
public class BankManager { 
    private unsafe Account* mAccounts; 
    public unsafe int GetA() { 
     return mAccounts->a; 
    } 
    public unsafe BankManager() { 
     mAccounts = (Account*)Marshal.AllocHGlobal(sizeof(Account)); 
    } 
    unsafe ~BankManager() { 
     if (mAccounts != null) { 
      Marshal.FreeHGlobal((IntPtr)mAccounts); 
      mAccounts = null; 
     } 
    } 
} 

Dưới đây, chúng tôi đã phân bổ các cấu trúc trong bộ nhớ không được quản lý. Điều này cho phép chúng tôi giữ một con trỏ đến nó mà chúng ta biết không thay đổi hoặc di chuyển. Chúng ta phải tự giải phóng cấu trúc khi chúng ta hoàn thành nó. Cùng một hướng dẫn phân bổ/miễn phí và marshalling sẽ cần phải được thực hiện để mAccounts-> mName, vì nó là làm thế nào một char * không quản lý (c-style string).

Tôi đã tạo cấu trúc có bố cục tuần tự đóng gói để làm cho hành vi của mã này gần hơn với C-đối tác, vì mã như trên thường sẽ chỉ được sử dụng khi thực hiện interop với điểm nhập DllImport gốc mà dự kiến ​​một cấu trúc cụ thể bố trí.

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