将 C++ 浮点数组成员直接编组到 c# 而无需复制

Marshalling a C++ float array member to c# directly without copying

我有一个矩阵 class,其中包含我想直接在 C# 包装器中公开的固定大小的浮点数组 class:

class Matrix
{
public:
    float data[16];
    ...
};

通常,在 C# 中包装此结构时,我会将包装类型定义为 POD 结构,该结构将利用 [StructLayout(LayoutKind.Sequential)] 并允许我直接编组 class 与 C++/C# 之间的编组.但是,c++ class 公开了其他类型(通过成员或函数),这些类型不能以这种方式编组,所以我不得不使用 PIMPL 机制,其中我的 C# class 包含一个不透明的指针(IntPtr ) 到底层 C++ 结构。但是,我仍然想直接 将c++ 结构的底层data 成员暴露给C# 对象。直接是指在 C# 中对 data 元素的修改将直接反映在底层 C++ 结构中(即 C# 成员不能是 C++ 成员的副本)。我的想法是像这样在 C# 中公开 data 成员:

public class Matrix
{
    protected HandleRef cptr; // opaque reference to equivalent C++ instance
    public float [] data
    {
        get
        {
            return Get_data(cptr);
        }
    }

    [DllImport(MY_CPP_DLL, EntryPoint = "CSharp_Get_data", CharSet = CHARSET)]
    [return: MarshalAs(UnmanagedType.LPArray, SizeConst = 16, ArraySubType = UnmanagedType.R4)]
    private static extern float [] Get_Data(HandleRef in_object);
};

我的 C++ dll 包含函数 CSharp_Get_data,如下所示:

DLLEXPORT float * STDCALL CSharp_Get_data(Matrix *obj)
{
    return obj->data;
}

这在 C++ 和 C# 中编译得很好,但是当我尝试在 C# 中做这样的事情时:

Matrix asdf = new Matrix();
asdf.data[0] = 2.0f;

我收到一个 System.Runtime.InteropServices.MarshalDirectiveException ,上面写着 Additional information: Cannot marshal 'return value': Invalid managed/unmanaged type combination.

有没有一种方法我可以使用这种方法而不遇到这个错误?我知道这是一个非常奇怪的用例,但我觉得这种方法似乎没有违反 MS 在这里描述的任何编组标准:https://docs.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-for-arrays.

感谢您的帮助。

除非 .NET 分配内存,否则您无法获得 float[]StructLayout(Sequential) 也是如此——float[16] 等固定大小的缓冲区与 float[].

不兼容

不过,如果您比 float[] 更想要方便和高效,您可以做的是使用新的零拷贝缓冲类型。您可以从指向第一个元素的指针加上元素的数量构造一个 System.Span<float> ,并且下标该跨度将使您可以直接访问内存。需要进行一些重构,但由于 Span<T> 可以同样很好地访问 .NET 数组,一旦您重写代码以使用 Span,它就可以存在于两个世界中(C# 和 C++ 分配的数据) .