如何解决C#PInvoke:传递包含引用实例的实例
我尝试了两天,通过PInvoke将包含C#端类实例(如果需要的话是.NET Core 3.1)的结构实例传递给C ++。在显示示例代码时,我将解释我的问题。我真的不知道我在这里想念什么。
相关的C#代码
如您所见,我有一个AppleStack
类,其中包含对Owner
类实例的引用,因此一个人可以拥有多堆苹果。各种AppleStack
实例应该可以作为参考传递给C ++代码,以便我可以在本地代码中修改它们的字段:
[StructLayout(LayoutKind.Sequential)]
class Owner
{
public int id;
public float money;
}
[StructLayout(LayoutKind.Sequential)]
struct AppleStack
{
public int stock;
public float price;
public Owner owner;
}
[DllImport("NativeDll.dll",CallingConvention = CallingConvention.Cdecl)]
private extern static void apple_buy(ref AppleStack apple,int amount);
[DllImport("NativeDll.dll",CallingConvention = CallingConvention.Cdecl)]
private extern static void apple_print(ref AppleStack apple);
到目前为止一切顺利。现在,我实例化了两个共享同一所有者的苹果。具体值无关紧要,但是如有必要,我可以提供此代码段。实例化之后,我按以下顺序调用C ++函数:
apple_print(ref apple1);
apple_buy(ref apple1,3);
apple_buy(ref apple1,2);
apple_print(ref apple1);
apple_print(ref apple2);
相关的C ++代码
在本机方面,我有两种匹配类型:
struct Owner
{
int id;
float money;
};
struct Apple
{
int stock;
float price;
Owner owner; // (Marker 1)
};
请注意,此处的owner字段是在堆栈而不是堆上分配的。这与我下面的问题有关。在我将展示购买方法之前。打印方法仅记录字段,因此我不会将其放在此处,但是当然,如果需要,我可以提供它:
void apple_buy(Apple* apple,int amount)
{
std::cout << "You bought apples (x" << amount << ") from " << apple->owner.id
<< " for " << (amount * apple->price) << "$!" << std::endl;
apple->stock -= amount;
apple->owner.money += amount * apple->price;
}
控制台输出
Stack of 7 apples for 2.1$ found!
Owner has id 10 and owns 225.53$ of money.
You bought apples (x3) from 10 for 0.9$!
You bought apples (x2) from 10 for 0.6$!
Stack of 2 apples for 0.6$ found!
Owner has id 10 and owns 227.03$ of money.
Stack of 11 apples for 4.4$ found!
Owner has id 10 and owns 225.53$ of money.
最后两行来自第二个堆栈。所有其他行都来自第一个堆栈。 编辑:购买后,两个堆栈的所有者应拥有相同的钱,但购买后,他同时拥有225.53 $和227.03 $。
我尝试过的替代解决方案
我从this question (also on StackOverflow)阅读到,您可以使用GCHandle
类来完成此操作。因此,例如,我将apple_print
方法更改为 private extern static void apple_print(IntPtr apple);
。然后,主要方法将像这样结束:
var handle1 = GCHandle.Alloc(apple1);
var handle2 = GCHandle.Alloc(apple2);
apple_print(GCHandle.ToIntPtr(handle1));
apple_buy(GCHandle.ToIntPtr(handle1),3);
apple_buy(GCHandle.ToIntPtr(handle1),2);
apple_print(GCHandle.ToIntPtr(handle1));
apple_print(GCHandle.ToIntPtr(handle2));
但这会导致完全废话,而且如果我仔细查看输出,甚至无法解决问题:
Stack of -2147424088 apples for -1.76037e-33$ found!
Owner has id -2147428072 and owns 8.1976e-43$ of money.
You bought apples (x3) from -2147428072 for 2.45928e-42$!
You bought apples (x2) from -2147428072 for 1.63952e-42$!
Stack of -2147424093 apples for -1.76037e-33$ found!
Owner has id -2147428072 and owns 4.91856e-42$ of money.
Stack of -2147424032 apples for -1.76037e-33$ found!
Owner has id -2147424093 and owns 8.1976e-43$ of money.
问题
- 为什么(标记1)可以正常工作?
Owner
类的内容是否被当作结构对待?如果我尝试将struct AppleStack
更改为一个类,然后传递没有ref的实例,则它将所有内容复制到本机端。 - 给定的示例确实有效,但是由于明显的原因,所有者被复制到第二个堆栈,而不是被共享为引用。解决此问题的最佳做法是什么?
- 如果GCHandle是答案,那么我不明白这一点是什么?
如果您需要更多信息,我将尝试尽快提供。我预先感谢所有尝试在此提供帮助的人:D
编辑1
[DllImport("NativeDll.dll",CallingConvention = CallingConvention.Cdecl)]
private extern static void apple_buy([In,Out] ref AppleStack apple,int amount);
以参数形式表示。然后,我没有在托管方分配所有者,而是向这些字段分配了null
。在本机方面,我将打印功能更改为
void apple_print(Apple* apple)
{
static Owner owner{ 10,225.0f };
apple->owner = &owner;
// Printing fields...
}
(我知道有点hacky,但我希望此示例能够完成工作)以及要构造的结构
struct Apple
{
int stock;
float price;
Owner* owner;
};
只要我不访问.NET Core中的字段,它就很好用。然后,输出(通过Console.WriteLine($"Owner has: {apple1.owner.money}$");
)是
Stack of 7 apples for 2.1$ found!
Owner has id 10 and owns 225$ of money.
You bought apples (x3) from 10 for 0.9$!
You bought apples (x2) from 10 for 0.6$!
Stack of 2 apples for 0.6$ found!
Owner has id 10 and owns 226.5$ of money.
Stack of 11 apples for 4.4$ found!
Owner has id 10 and owns 226.5$ of money.
Owner has: 4,5912E-41$
因此所有者存在,但数据解释不正确。我的目标是我可以按原样传递实例,以实现最佳性能。我需要以某种方式保持一对多的关系。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。