同志们,下面的问题让我很感兴趣:
假设我们有一个为了结构起见的例子,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
其实类似于简单的改变地方的变量
现在主要的问题是:有了指针,我怎样才能得到一个对象,就像在结构的情况下一样?
问题是
- 创建指向类的指针 - 不可能(错误CS0208)
- 而且Marshal.PtrToStructure也只为结构设计
所以我正在寻找这样的东西:
IntPtr* pointer = ...;
// Я знаю, что это не работает, это просто псевдо-код
MyClass my = *((MyClass*)pointer);
my = Marshal.PtrToClass<MyClass>(new IntPtr(pointer));
实际上,这是否可能(C#我认为根本不可能),如果可以,如何?
即使是最疯狂的想法也受到欢迎!
有几个原因导致这是不可能的。
Marshal.PtrToStructure<Point>创建结构的副本。例如,如果将结果分配给局部变量,则结构的副本将被压入堆栈。调用此方法后,您可以更改从中复制结构的内存,并且您的副本不会发生任何事情。如您所见,从程序完整性的角度来看,操作本身是安全的。Marshal.PtrToClass<MyClass>您很可能是指指针取消引用(类似于 C++)。垃圾收集器可以随时停止整个程序的执行,并在内存中移动对象。可能会发生这样的事情:Marshal.PtrToStructure被发明来与本机代码交互。.NET-通常意义上的类不能存在于本机代码中,因此它们PtrToClass不存在。还有一种简单的使用方式
GCHandle,就是禁止内存中的对象移动。但在 C# 中,您可以这样做:
使用示例:
您无法将指针转换为对象,原因很简单,即您无法从任何地方获得指向对象的指针:.net 根本没有给您该选项。
但是如果你真的不需要指向对象的指针,而只需要 IntPtr,你可以使用 GCHandle。
创建 GCHandle:
删除 GCHandle(如果没有完成 - 将会有内存泄漏!)
转换为对象:
无法直接创建指向类的指针,因为它由垃圾收集器管理并且可以在内存中重新定位。但是,可以使用 Pinned GCHandle 固定一个类并获得一个指向被固定对象的指针 - 但为此,该类必须仅包含简单类型并具有一个属性
[StructLayout(LayoutKind.Sequential)](尽管有名称,它也可以应用于一个类)。严格来说,并非如此。如果类满足相同的条件,Marshal.PtrToStructure 也可以使用:它们只包含简单类型并且具有
[StructLayout(LayoutKind.Sequential)].下面是一个使用类指针的例子:
与接受的答案相反,这使用指向类数据本身的指针,而不是对象的标头。此外,调用 Marshal.PtrToStructure 会创建对象的新副本,而不是取消引用指向现有对象的指针。