C# 无法获取托管类型(';T';)的地址、大小或声明指向该类型的指针
为什么不允许这样做:C# 无法获取托管类型(';T';)的地址、大小或声明指向该类型的指针,c#,.net,C#,.net,为什么不允许这样做: private static unsafe byte[] ConvertStruct<T>(T str) where T: struct { T* ptr = &str; int size = Marshal.SizeOf(str); var arr = new byte[size]; Marshal.Copy((IntPtr) ptr, arr, 0, size);
private static unsafe byte[] ConvertStruct<T>(T str) where T: struct
{
T* ptr = &str;
int size = Marshal.SizeOf(str);
var arr = new byte[size];
Marshal.Copy((IntPtr) ptr, arr, 0, size);
return arr;
}
私有静态不安全字节[]ConvertStruct(T str),其中T:struct
{
T*ptr=&str;
int size=Marshal.SizeOf(str);
var arr=新字节[大小];
封送处理副本((IntPtr)ptr,arr,0,size);
返回arr;
}
我得到编译错误“无法获取托管类型('T')的地址、大小或声明指向托管类型('T')的指针”
还是“结构”约束还不够?在某种意义上,它仍然可以是一个托管结构
我是否正确理解了我的结构可能不可空投?如果是这样,blittable是否应该是一个有效的约束 来自C#规范,第18.2节:
不允许指针指向引用或包含引用的结构,指针的referent类型必须是非托管类型
非托管类型是以下类型之一:
•sbyte
,byte
,short
,ushort
,int
,uint
,long
,ulong
,float
,double
,decimal
,或bool
•任何枚举类型
•任何指针类型
•任何非构造类型且仅包含非托管类型字段的用户定义结构类型
没有真正的方法通过一般约束来表达这一点。即使这个定义看起来与的任何定义都非常相似,但值得注意的是,这个特定的概念在C#Spec中没有定义(甚至没有引用)。例如,从MSDN页面上,我们可以看到“一个blittable类型的一维数组”是可blittable的,但上面的规范定义似乎排除了数组。就像不信者Damian_所说的,您不能为托管对象或包含托管字段的结构创建非托管指针。正如我在评论中所说的,没有可以实现的通用约束来确保编译时的安全性,以确保结构满足这些要求。因此,不能创建指向泛型类型的指针 然而,可能有一个解决办法。该方法将获取结构对象并将其数据复制到
IntPtr
地址。您可以将其与Marshall.AllocHGlobal
结合使用,以尽可能实现所需的功能:
private static byte[] ConvertStruct<T>(ref T str) where T : struct
{
int size = Marshal.SizeOf(str);
IntPtr arrPtr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(str, arrPtr, true);
var arr = new byte[size];
Marshal.Copy(arrPtr, arr, 0, size);
Marshal.FreeHGlobal(arrPtr);
return arr;
}
但是,第一种方法的好处是,它不需要
不安全的上下文来工作。(如果你关心这类事情。)试试这样的事情。通过编组,您可以避免不安全的关键字
private static byte[] ConvertStruct<T>(T str) where T : struct
{
int size = Marshal.SizeOf(str);//sizeof
int ptr = (int)Marshal.AllocCoTaskMem(size);//allocate memory before past the structure there
Marshal.StructureToPtr(str, (IntPtr)ptr, true);//alloc your structure
byte[] res=new byte[size];//your result
for (int i = 0; i < size; i++)
{
res[i] = Marshal.ReadByte((IntPtr)ptr);//read byte from memory
ptr++;//offset
}
return res;
}
如果您的结构包含具有umnmanaged类型的字段,请使用属性正确封送具有未损坏类型的所有字段(例如,字符串作为LPWStr)。如果您的结构是完全非托管的,则需要使用MarshalAs封送它的所有字段。只有当结构只有非托管成员(即没有类类型的字段或类似字段)时,才能创建指向该结构的指针。由于泛型不能将类型限制为仅传递此规则的结构,编译器别无选择,只能完全禁止它。从那时起,您将能够使用where t:unmanaged
。注意:对于希望查看地址以确保了解引擎盖下发生的情况的任何人,请看这两个视频,不要为此绞尽脑汁。(视频1:youtube.com/watch?v=h6aXzd1nTXQ)(视频2:youtube.com/watch?v=mvieNUe9Urs)。这给了我想知道的一切。我来自C++背景,当我不能用表窗口随意查看内存地址时,我感到非常沮丧。在讨论内存时,直观地了解背景中发生的事情非常有用。您可以使用Marshal.StructureToPtr
了解一些内容,但我认为您不需要AllocHGlobal
,如果锁定它,可以直接复制到托管阵列。此外,如果确实使用AllocHGlobal
分配内存,请确保也使用FreeHGlobal
@KrisVandermotten Nice调用释放内存。我试图找出如何获取数组的指针,但完全忘记了固定。此外,OP的签名会导致结构的副本。如果它是通过引用传递的,那么也可以避免该副本<代码>静态不安全字节[]ConvertStruct(ref T str),其中T:struct
看起来这会更慢,不是吗?Marshal.StructureToPtr也是一个简单的低级字节复制,还是它反映了struct的所有属性?我猜,LayoutKind.Sequential只是按照字段值的定义顺序将其复制到内存中。这就是为什么我们使用Marshal.ptrtoscture或Marshal.Copy after StructureToPtr。但是,是的,元帅。拷贝比我的缓存读取快。
private static byte[] ConvertStruct<T>(T str) where T : struct
{
int size = Marshal.SizeOf(str);//sizeof
int ptr = (int)Marshal.AllocCoTaskMem(size);//allocate memory before past the structure there
Marshal.StructureToPtr(str, (IntPtr)ptr, true);//alloc your structure
byte[] res=new byte[size];//your result
for (int i = 0; i < size; i++)
{
res[i] = Marshal.ReadByte((IntPtr)ptr);//read byte from memory
ptr++;//offset
}
return res;
}
[StructLayout(LayoutKind.Sequential)]
[Serializable]
public struct StrStruct
{}