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