Товарищи, стал мне интересен следующий вопрос:
Положим, есть у нас примера ради структура System.Drawing.Point
Мы спокойно можем баловаться с ней следующим образом:
// Инициализируем нашу структурку
Point point = new Point(2, 3);
// Получаем ее адрес
Point* pointer = &point;
// Получим ее данные
int x = ((int*)pointer)[0]; // 2
int y = ((int*)pointer)[1]; // 3
// Получаем ту же структуру, разыменовав указатель
Point copied = *pointer;
// Или так (получаем нулевую `Point` по указателю)
copied = pointer[0];
// Или даже так
copied = Marshal.PtrToStructure<Point>(new IntPtr(pointer));
В общем, имея указатель, мы спокойно можем получить структуру в явном виде
Теперь же отойдем от типов значения и перейдем к типам ссылочным, то есть поговорить я хочу о подобных механизмах для классов
Оперируя инстансами классов, мы на деле оперируем их ссылками, то есть, получив указатель, мы получим указатель на ссылку на определенный участок памяти, где как раз и можно найти данные объекта
То есть следующий псевдо-код:
// Обозначим instance'ы классов
MyClass my0 = new MyClass { A = 2 };
MyClass my1 = new MyClass { A = 3 };
// Некоторые действия
IntPtr* ptr0 = ...;
IntPtr* ptr1 = ...;
IntPtr tmp = *ptr0;
ptr0[0] = ptr1[0];
ptr1[0] = tmp;
Console.WriteLine(my0.A); // 3
Console.WriteLine(my1.A); // 2
На деле аналогичен простой смене переменных местами
И вот теперь главный вопрос: каким образом, имея указатель, я могу также, как и в случае со структурой, получить объект?
Проблема в том, что
То есть я ищу нечто такое:
IntPtr* pointer = ...;
// Я знаю, что это не работает, это просто псевдо-код
MyClass my = *((MyClass*)pointer);
my = Marshal.PtrToClass<MyClass>(new IntPtr(pointer));
Собственно, возможно ли такое в C#
(не думаю, что прямо совсем никак) и, если да, то как?
Приветствуются даже самые безумные идеи!
Есть несколько причин, почему это невозможно.
Marshal.PtrToStructure<Point>
создает копию структуры. Например,
если вы присвоите результат в локальную переменную, копия
структуры переместится в стек. После вызова этого метода, вы можете
изменять память, откуда была скопирована структура, и с вашей копией
ничего не случиться. Как видно, сама операция безопасна с точки зрения целостности программы.
Под Marshal.PtrToClass<MyClass>
вы скорее всего подразумеваете разыменование указателя (по аналогии с С++). Сборщик мусора может остановить выполнение целой программы в любом месте, и переместить объекты в памяти. Может случиться что-то такое:
IntPtr ptr = получить_указатель_на_экземпляр_класса();
// здесь сборщик мусора остановил потоки, и переместил класс в новое место
MyClass instance = Marshal.PtrToClass<MyClass>(ptr); // ptr указывает непонятно куда
Marshal.PtrToStructure
был придуман для взаимодействия с нативным кодом. .NET
-классы не могут в обычном понимании существовать в нативном коде, поэтому PtrToClass
не существует.
Но на C# можно и такое сделать:
private static unsafe T PtrToClass<T>(IntPtr ptr)
{
T temp = default(T);
TypedReference tr = __makeref(temp);
Marshal.WriteIntPtr(*(IntPtr*)(&tr), ptr);
T instance = __refvalue(tr, T);
return instance;
}
Пример использования:
class MyClass
{
public int Value;
}
static unsafe void Main(string[] args)
{
var instance1 = new MyClass { Value = 123 };
var instance2 = new MyClass { Value = 321 };
TypedReference tr1 = __makeref(instance1);
TypedReference tr2 = __makeref(instance2);
IntPtr ptr1 = **(IntPtr**)(&tr1);
IntPtr ptr2 = **(IntPtr**)(&tr2);
var instance3 = PtrToClass<MyClass>(ptr1);
var instance4 = PtrToClass<MyClass>(ptr2);
Console.WriteLine(instance3.Value);
Console.WriteLine(instance4.Value);
}
Вы не можете превратить указатель в объект по той простой причине что вам неоткуда взять указатель на объект: .net просто не дает такой возможности.
Но если на самом деле вам не нужен именно указатель на объект, а достаточно лишь IntPtr - можно использовать GCHandle.
Создание GCHandle:
GCHandle.Alloc(obj).ToIntPtr()
Удаление GCHandle (если не сделать - будет утечка памяти!)
GCHandle.FromIntPtr(ptr).Free()
Преобразование в объект:
GCHandle.FromIntPtr(ptr).Target
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
Есть словарь Dictionary<string, int>,как можно отсортировать элементы словаря для вывода в консоль по Key в алфавитном порядке по возрастанию(от "a" до "z") без...
Как можно реализовать перемещение объектов UI мышкой? Например в инвентаре есть 2 слота, нужно из первого переместить объект во второй слот
Каким образом можно правильно описать метод для объединения пути к файлу?