无法获取地址,获取大小或声明指向托管类型的指针

我已经做了一些研究,但现在我仍然坚持为什么我仍然得到这个错误。 我有一个具有以下属性的结构:

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

即使将我的类帐户转换为结构,对类BankManager中的属性使用“unsafe”,并告诉编译器它可以使用不安全的代码(在属性 – >构建中),我仍然会收到此错误

 *mAccounts 

任何想法为什么? 我很确定我在结构中使用的所有类型都是合法的,可以在c#中使用指针。 提前致谢!

Account类中的字符串会导致此问题。 要了解原因,您需要了解垃圾收集器的工作原理。 它通过跟踪对象的引用来发现垃圾。 mName和mDateCreated就是这样的引用。 mBalance和mAccountNumber 不是 ,这些字段是值类型。 而且,最重要的是,BankManager.mAccounts字段不是,它是一个指针。

因此编译器可以预先告诉垃圾收集器永远不会看到字符串引用。 因为这样做的唯一方法是通过mAccount字段而不是参考。

唯一能解决的问题是严格限制自己的价值类型。 对字符串执行此操作的唯一方法是使用Marshal.StringToCoTaskMemUni()将它们分配到非托管内存中,并将IntPtr存储在字段中。 它现在从垃圾收集器无法触及,无法被它移动。 你现在也有释放那个字符串的负担。

很明显,这是不实际的,容易导致泄漏,这种问题在C程序中很常见。 不知道你为什么要追求这个,但要记住,对象的引用已经是一个简单的指针,所以你不是通过自己使用指针获得任何东西。

字符串是.NET中的引用类型,对于struct指针是非blittable的。 有关要执行的操作的值类型列表,请参阅Blittable和Non-Blittable Types 。

除非您有特殊的业务要求,否则您应该坚持使用托管内存以实现可维护性和一般的健全性。

使用private unsafe fixed char mName[126];
字符串是托管类型,非固定数组也是如此。

包含可以有指针的类型的struct是错误的,因为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; } } } 

这里我们在非托管内存中分配了struct。 这允许我们保持指向它的指针,我们知道它不会改变或移动。 完成后我们必须手动释放结构。 需要对mAccounts-> mName执行相同的手动alloc / free和marshalling,因为它是非托管char *(c样式字符串)的方式。

我使结构具有打包顺序布局,以使该代码的行为更接近它的C对应物,因为上面的代码通常仅在与需要特定结构布局的本机C DllImport入口点进行互操作时使用。