我有一个矩阵类,其中包含一个固定大小的 float 组,我想在 C# 包装器类中直接公开它:
class Matrix
{
public:
float data[16];
...
};
通常,在 C# 中包装此结构时,我会将包装类型定义为 POD 结构,该结构将利用 [StructLayout(LayoutKind.Sequential)]
并允许我直接将类编码到和来自 C++/C#。但是,c++ 类公开了其他类型(通过成员或函数),这些类型无法以这种方式编码,因此我不得不使用 PIMPL 机制,其中我的 C# 类包含指向底层 C++ 结构的不透明指针 (IntPtr)。但是,我仍然想直接将c++ 结构的底层data
成员暴露给C# 对象。所谓直接,是指对 C# 中 data
元素的修改将直接反射(reflect)在底层 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,它指出 附加信息:无法编码(marshal)“返回值”:无效的托管/非托管类型组合。
有没有一种方法我可以使用这种方法而不遇到这个错误?我知道这是一个非常奇怪的用例,但我觉得这种方法似乎没有违反 MS 在此处描述的任何编码标准:https://learn.microsoft.com/en-us/dotnet/framework/interop/default-marshaling-for-arrays .
感谢您的帮助。
最佳答案
你无法获得 float[]
除非.NET 分配内存。 StructLayout(Sequential)
也是如此, 也是——固定大小的缓冲区,例如 float[16]
与 float[]
不兼容.
但是,如果您更想要方便和高效,您可以做什么 float[]
, 是使用新的零拷贝缓冲类型。你可以构造一个 System.Span<float>
从指向第一个元素的指针加上元素的数量,然后下标该跨度将使您可以直接访问内存。需要进行一些重构,但由于 Span<T>
一旦重写代码以使用 Span
,就可以同样很好地访问 .NET 数组,它可以存在于两个世界中(C# 和 C++ 分配的数据)。
关于c# - 将 C++ float 组成员直接编码到 c# 而无需复制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52414217/