C#: использование типов указателей в качестве полей?
В C# можно объявить структуру (или класс), которая имеет член типа указателя, например:
unsafe struct Node
{
public Node* NextNode;
}
Это когда-нибудь безопасно? unsafe
флаг..) использовать эту конструкцию? Я имею в виду долгосрочное хранение в куче. Из того, что я понимаю, GC может свободно перемещать вещи, и, хотя он обновляет ссылки на что-то, что было перемещено, он также обновляет указатели? Я предполагаю нет, что сделало бы эту конструкцию очень небезопасной, верно?
Я уверен, что есть альтернативы для этого, но назовите это болезненным любопытством.
РЕДАКТИРОВАТЬ: Кажется, есть некоторая путаница. Я знаю, что это не очень хорошая конструкция, я просто хочу знать, является ли она когда-либо безопасной конструкцией, то есть: гарантированно ли указатель будет продолжать указывать на то, на что вы изначально указывали?
Исходный C-код использовался для обхода дерева (сначала глубиной) без рекурсии, где дерево хранится в массиве. Затем массив перемещается путем увеличения указателя, если только не выполнено определенное условие, тогда указатель устанавливается на NextNode, где обход продолжается. Конечно, то же самое можно сделать в C#:
struct Node
{
public int NextNode;
... // other fields
}
Где int
это индекс в массиве следующего узла. Но по причинам производительности, я бы в конечном итоге возился с указателями и fixed
массивы, чтобы избежать проверки границ в любом случае, и оригинальный C-код казался более естественным.
5 ответов
Всегда ли безопасно использовать эту конструкцию? Я имею в виду для длительного хранения в куче.
Да. Делать это обычно глупо, больно и не нужно, но это возможно.
Из того, что я понимаю, GC может свободно перемещать вещи, и, хотя он обновляет ссылки на что-то, что было перемещено, он также обновляет указатели?
Нет. Вот почему мы заставляем вас пометить его как небезопасный.
Я предполагаю нет, что сделало бы эту конструкцию очень небезопасной, верно?
Правильный.
Я уверен, что есть альтернативы для этого, но назовите это болезненным любопытством.
Там, безусловно, есть.
гарантированно ли указатель будет продолжать указывать на то, на что вы изначально указывали?
Нет, если вы не уверены, что это произойдет Есть два способа сделать это.
Способ первый: скажите сборщику мусора, чтобы он не двигал памятью. Есть два способа сделать это:
Зафиксируйте переменную с помощью оператора "fixed".
Используйте сервисы взаимодействия для создания дескриптора gc для структур, которые вы хотите сохранить в одном месте.
Выполнение любого из этих действий с высокой вероятностью разрушит производительность сборщика мусора.
Способ второй: не берите ссылки на память, которую может переместить сборщик мусора. Есть два способа сделать это:
Берите только адреса локальных переменных, параметров значений или блоков, выделенных стеком. Конечно, при этом вам необходимо убедиться, что указатели не доживут дольше, чем соответствующий фрейм стека, в противном случае вы ссылаетесь на мусор.
Выделите блок из неуправляемой кучи, а затем используйте указатели внутри этого блока. В сущности, реализовать свой собственный менеджер памяти. Вы должны правильно реализовать свой новый менеджер памяти. Быть осторожен.
Некоторые очевидные проверки целостности были исключены. Очевидная проблема с этим заключается в том, что вы должны выделять больше, чем вам нужно, потому что вы не можете перераспределить буфер, как предполагает фиксированное ключевое слово.
public unsafe class NodeList
{
fixed Node _Nodes[1024];
Node* _Current;
public NodeList(params String[] data)
{
for (int i = 0; i < data.Length; i++)
{
_Nodes[i].Data = data[i];
_Nodes[i].Next = (i < data.Length ? &_Nodes[i + 1] : null);
}
_Current = &_Nodes[0];
}
public Node* Current()
{
return _Current++;
}
}
public unsafe struct Node
{
public String Data;
public Node* Next;
}
Да, сборщик мусора может перемещать объекты и, нет, он не будет обновлять ваши указатели. Вы должны исправить объекты, на которые вы указываете. Более подробную информацию можно найти в этом объяснении управления памятью.
Вы можете исправить объекты, как это:
unsafe {
fixed (byte* pPtr = object) {
// This will fix object in the memory
}
}
}
Преимуществами указателей обычно являются производительность и взаимодействие с другим небезопасным кодом. Там не будет никаких проверок и т. Д., Ускоряющих ваш код. Но так же, как если бы вы программировали, например, на C, вы должны быть очень осторожны в том, что вы делаете.
Опасная идея, но она может сработать
Когда ваш массив структур превышает определенный размер (85000 байт), он будет размещен в куче больших объектов, где блоки сканируются и собираются, но не перемещаются...
Связанная статья указывает на опасность того, что более новая версия CLR может переместить материал на LOH...
Почему бы и нет:
struct Node
{
public Node NextNode;
}
или по крайней мере:
struct Node
{
public IntPtr NextNode;
}
Вы можете использовать фиксированный оператор, чтобы помешать GC перемещать указатели.