微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

C#PInvoke:传递包含引用实例的实例

如何解决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. 为什么(标记1)可以正常工作? Owner类的内容是否被当作结构对待?如果我尝试将struct AppleStack更改为一个类,然后传递没有ref的实例,则它将所有内容复制到本机端。
  2. 给定的示例确实有效,但是由于明显的原因,所有者被复制到第二个堆栈,而不是被共享为引用。解决此问题的最佳做法是什么?
    • 如果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 举报,一经查实,本站将立刻删除。