C# 无法获取托管类型的地址、获取其大小或声明指向托管类型的指针
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13299153/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Cannot take the address of, get the size of, or declare a pointer to a managed type
提问by SantasNotReal
I've done a fair bit of research, but am stuck now as to why I'm still getting this error. I have a struct with the following attributes:
我已经做了一些研究,但现在我不明白为什么我仍然收到这个错误。我有一个具有以下属性的结构:
struct Account
{
//private attributes
private double mBalance;
private int mAccountNumber;
private string mName;
private string mDateCreated;
}
and am trying to do the following:
并且正在尝试执行以下操作:
class BankManager
{
//private attributes
private unsafe Account *mAccounts;
private unsafe bool *mAccountsAvailable;
private int mNumberAccounts;
}
Even after turning my class Account to a struct, using "unsafe" for the attributes in class BankManager, and telling the compiler it can use unsafe code (in properties -> Build), I'm still getting this error at
即使将我的类 Account 转换为结构体,对类 BankManager 中的属性使用“不安全”,并告诉编译器它可以使用不安全的代码(在属性 -> 构建中),我仍然收到此错误
*mAccounts
Any ideas as to why? I'm pretty sure all the types I'm using in the struct are legal to have pointers to in c#. Thanks in advance!
关于为什么的任何想法?我很确定我在结构中使用的所有类型在 c# 中都有指向的指针是合法的。提前致谢!
采纳答案by Hans Passant
The strings in the Account class cause this problem. To understand why, you need to understand how the garbage collector works. It discovers garbage by tracking references to objects. The mName and mDateCreated are such references. The mBalance and mAccountNumber are not, those fields are value types. And, most importantly, the BankManager.mAccounts field is not, it is a pointer.
Account 类中的字符串会导致此问题。要了解原因,您需要了解垃圾收集器的工作原理。它通过跟踪对对象的引用来发现垃圾。mName 和 mDateCreated 就是这样的引用。mBalance 和 mAccountNumber不是,这些字段是值类型。而且,最重要的是,BankManager.mAccounts 字段不是,它是一个指针。
So the compiler can tell up front that the garbage collector will neverbe able to see the string references. Because the only way to do so is to go through the mAccount field and its not a reference.
所以编译器可以预先告诉垃圾收集器将永远无法看到字符串引用。因为这样做的唯一方法是通过 mAccount 字段而不是引用。
The only cure for this is to limit yourself strictly to value types. The only way to do that for strings is to allocate them in unmanagedmemory with, say, Marshal.StringToCoTaskMemUni() and store the IntPtr in the field. It is now out of reach from the garbage collector and cannot get moved by it. You'll now also have the burden of releasing that string.
解决这个问题的唯一方法是严格限制自己使用值类型。对字符串执行此操作的唯一方法是使用 Marshal.StringToCoTaskMemUni()在非托管内存中分配它们并将 IntPtr 存储在字段中。它现在远离垃圾收集器并且无法被它移动。您现在还需要承担释放该字符串的责任。
Clearly that's not practical and prone to cause leaks, the kind of problem that's so common in C programs. Not sure why you are pursuing this at all but do keep in mind that a reference to an object is already a simple pointer so you are not gaining anything by using pointers yourself.
显然,这不切实际,而且容易导致泄漏,这种问题在 C 程序中很常见。不知道您为什么要这样做,但请记住,对对象的引用已经是一个简单的指针,因此您自己使用指针不会获得任何东西。
回答by Xint0
You are wrong about the struct containing types that can have pointers, because a stringis a managed type which cannot have a pointer reference.
你对包含可以有指针的类型的结构是错误的,因为 astring是一种不能有指针引用的托管类型。
回答by avishayp
Use private unsafe fixed char mName[126];
Strings are managed types, and so are non-fixed arrays.
使用private unsafe fixed char mName[126];
字符串是托管类型,非固定数组也是。
回答by hyru
Strings are reference types in .NET and are non-blittable for struct pointers. See Blittable and Non-Blittable Typesfor a list of value types for what you want to do.
字符串是 .NET 中的引用类型,对于结构指针是不可 blittable 的。有关您想要执行的操作的值类型列表,请参阅Blittable 和非 Blittable 类型。
Unless you have special business requirements, you should stick with managed memory for maintainability and general sanity.
除非您有特殊的业务需求,否则您应该坚持使用托管内存以实现可维护性和一般理智。
回答by David Jeske
Managed data does not stay in a fixed location, as the copying collector can move things around. This is equally true of managed boxed value types. Managed unboxed value-types can only live on the stack or inside other objects. They only have fixed locations if they are in the stack.
托管数据不会停留在固定位置,因为复制收集器可以四处移动。这同样适用于托管盒装值类型。托管未装箱值类型只能存在于堆栈中或其他对象内。如果它们在堆栈中,它们只有固定的位置。
In order to create a heap-allocated struct which has a fixed location from which you can take a pointer which will continue to be valid, you have to allocate it in unmanagedmemory. However, once you allocate it in unmanaged memory, you can't put managed pointers in it anymore (aka, you can't use string), because the garbage collector won't know about those pointers so it won't update them when it moves managed objects around during compaction.
为了创建一个具有固定位置的堆分配结构,您可以从中获取一个将继续有效的指针,您必须在非托管内存中分配它。但是,一旦将它分配到非托管内存中,就不能再将托管指针放入其中(也就是说,不能使用字符串),因为垃圾收集器不会知道这些指针,因此在它在压缩期间移动托管对象。
For example, this is a valid (though not necessarily good) thing to do:
例如,这是一个有效的(虽然不一定好)的做法:
[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;
}
}
}
Here we have allocated the struct in unmanaged memory. This allows us to hold a pointer to it which we know does not change or move. We have to manually free the struct when we are done with it. The same manual alloc/free and marshalling would need to be done to mAccounts->mName, since it is how an unmanaged char* (c-style string).
这里我们在非托管内存中分配了结构体。这允许我们持有一个指向它的指针,我们知道它不会改变或移动。完成后,我们必须手动释放结构。需要对 mAccounts->mName 执行相同的手动分配/释放和编组,因为它是非托管 char*(c 样式字符串)的方式。
I made the struct have packed sequential layout to make the behavior of this code closer to it's C-counterpart, because code like the above would normally only be used when doing interop with a native C DllImport entrypoint that expects a particular struct layout.
我使结构具有打包顺序布局,以使此代码的行为更接近它的 C 对应部分,因为上述代码通常仅在与需要特定结构布局的本机 C DllImport 入口点进行互操作时使用。

