Не может взять адрес, получить размер, или объявить указатель на управляемый тип

Я провел немало исследований, но теперь застрял в том, почему я все еще получаю эту ошибку. У меня есть структура со следующими атрибутами:

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

и я пытаюсь сделать следующее:

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

даже после превращения моей учетной записи класса в структуру, используя "небезопасный" для атрибутов в class BankManager и сообщая компилятору, что он может использовать небезопасный код (в свойствах -> Build), я все еще получаю эту ошибку в

*mAccounts

есть идеи, почему? Я уверен, что все типы, которые я использую в структуре, являются законными для указателей на c#. Заранее спасибо!

5 ответов


строки в классе Account вызывают эту проблему. Чтобы понять, почему, вам нужно понять, как работает сборщик мусора. Он обнаруживает мусор, отслеживая ссылки на объекты. Такими ссылками являются mName и mDateCreated. В mBalance и mAccountNumber являются не, эти поля являются типами значений. И, самое главное, банковский менеджер.поле mAccounts не является, это указатель.

Так что компилятор может сказать, что сборщик мусора будет никогда иметь возможность видеть ссылки на строки. Потому что единственный способ сделать это-пройти через поле mAccount и не ссылка.

единственное лекарство от этого-строго ограничить себя типами значений. Единственный способ сделать это для строк-выделить их в неуправляемые память с, скажем, маршалом.StringToCoTaskMemUni () и сохраните IntPtr в поле. Теперь он находится вне досягаемости от сборщика мусора и не может быть перемещен им. Теперь вы также имейте бремя освобождения этой строки.

очевидно, что это не практично и склонно вызывать утечки, проблема, которая так распространена в программах на C. Не уверен, почему вы преследуете это вообще, но имейте в виду, что ссылка на объект уже является простым указателем, поэтому вы ничего не получаете, используя указатели самостоятельно.


строки являются ссылочными типами в .NET и не являются blittable для указателей структуры. См.Blittable и Non-Blittable типы для списка типов значений для того, что вы хотите сделать.

Если у вас нет специального бизнес-требований, вы должны придерживаться управляемой памяти для ремонтопригодности и общей вменяемости.


использовать private unsafe fixed char mName[126];
Строки-это управляемые типы, а также нефиксированные массивы.


вы ошибаетесь относительно структуры, содержащей типы, которые могут иметь указатели, потому что a string управляемый тип, который не может иметь ссылку на указатель.


управляемые данные не остаются в фиксированном месте, так как копирующий коллектор может перемещать вещи. Это относится и к управляемым коробочным типам значений. Управляемые распакованные типы значений могут жить только в стеке или внутри других объектов. Они имеют фиксированные местоположения, только если они находятся в стеке.

чтобы создать структуру, выделенную кучей, которая имеет фиксированное местоположение, из которого вы можете взять указатель, который будет продолжать быть действительным, вы должны выделить его в неуправляемые память. , как только вы выделите его в неуправляемой памяти, вы больше не можете поместить в него управляемые указатели (ака, вы не можете использовать строку), потому что сборщик мусора не будет знать об этих указателях, поэтому он не будет обновлять их, когда он перемещает управляемые объекты во время уплотнения.

например, это действительная (хотя и не обязательно хорошая) вещь:

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

здесь мы выделили структуру в неуправляемой памяти. Этот позволяет нам удерживать указатель на него, который, как мы знаем, не изменяется и не перемещается. Мы должны вручную освободить структуру, когда закончим с ней. То же самое ручное выделение/свободное и маршалинг должно быть сделано для mAccounts->mName, так как это как неуправляемый char* (строка c-стиля).

Я сделал структуру упакованной последовательной компоновкой, чтобы сделать поведение этого кода ближе к его c-аналогу, потому что код, подобный приведенному выше, обычно используется только при взаимодействии с родным C DllImport entrypoint, который ожидает определенный макет структуры.