不能获取地址,获取大小,或声明指向托管类型('T')的指针

时间:2017-02-10 08:40:00

标签: c# .net

为什么哦为什么不允许这样做:

import csv

read_a = csv.reader(filaA, delimiter='\t')
read_b = csv.reader(filaB, delimiter='\t')

dict_a = {tuple(row[:2]): row for row in read_a}
dict_b = {tuple(row[:2]): row for row in read_b}

shared_keys = set(dict_a) & set(dict_b)  # intersection of keys

writer = csv.writer(open('file.csv', 'w'), delimiter='\t')
writer.writerows(dict_a[k] + dict_b[k] for k in shared_keys)

我收到编译错误“无法获取地址,获取大小,或声明指向托管类型('T')的指针”

或“结构”约束还不够?从某种意义上说,它仍然可以是一个托管结构?

我是否正确理解我的结构可能不会被搞砸?如果是这样,blittable应该不是一个有效的约束?

3 个答案:

答案 0 :(得分:5)

就像Damian_The_Unbeliever所说,你不能创建托管对象或包含托管字段的结构的非托管指针。就像我在评论中所说的那样,没有可以实现的通用约束,这将确保编译时安全性,以确保结构符合这些要求。因此,您无法指向泛型类型。

然而,可能有一种解决方法。 Marshal.StructureToPtr方法将获取结构对象并将其数据复制到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;
}

正如Kris Vandermotten指出的那样,Marshal.AllocHGlobal实际上并不是必需的。您可以固定数组,为其获取IntPtr,然后直接复制到IntPtr

private static unsafe byte[] ConvertStruct<T>(ref T str) where T : struct
{
    int size = Marshal.SizeOf(str);
    var arr = new byte[size];

    fixed (byte* arrPtr = arr)
    {
        Marshal.StructureToPtr(str, (IntPtr)arrPtr, true);
    }

    return arr;
}

第一种方法的好处是,它不需要unsafe上下文才能工作。 (如果你关心那种事情。)

答案 1 :(得分:3)

来自C#Spec,第18.2节:

  

不允许指针指向引用或包含引用的结构,指针的引用类型必须是非托管类型。

     

...

     

非托管类型是以下之一:

     

sbytebyteshortushortintuintlong,{{1} },ulongcharfloatdoubledecimal

     

•任何枚举类型

     

•任何指针类型

     

•任何用户定义的 struct-type ,它不是构造类型,只包含 unmanaged-types 字段。

并且没有真正的方式通过通用约束来表达这一点。即使这个定义看起来与"blittable"的任何定义基本相似,但值得注意的是,在C#规范中没有定义(甚至引用)该特定概念。例如,从MSDN页面我们可以看到&#34; blittable类型的一维数组&#34;是blittable,但上面的规范定义似乎排除了数组。

答案 2 :(得分:1)

尝试这样的事情。通过编组,您可以避免使用不安全的关键字

 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属性,如下所示

 [StructLayout(LayoutKind.Sequential)]
[Serializable]
public struct StrStruct
{}

如果您的结构包含具有umnmanaged类型的字段,请使用MarshalAs属性来正确编组所有字段和未映射类型(例如,字符串作为LPWStr)。如果您的结构完全不受管理,则需要使用MarshalAs编组其所有字段。