c# - 从 C# 到 C++ 的数组再返回,没有不安全的代码

标签 c# c++ arrays unity3d

<分区>

我知道这个问题已经被问过并且可能被认为是重复的,但是在阅读了其他答案之后我仍然很困惑并且不理解那里提供的解释。

如果有人能提供示例代码 一些解释,我将不胜感激。

我正在处理以下任务:
我在 Unity 中有一个纹理/纹理(无法从驱动器加载,动态生成)并将其转换为数组。它可以是 int、byte 或 float 的数组。

目前,我在 C# 中进行对象检测,但我想让它更快。因此,我想做以下事情。
访问 C++ 代码(从 dll,我刚刚开始我的 C++ 之旅)并运行一个方法,该方法将获取我的输入数组,进行计算并将新数组(旧数组未修改)返回给 C# 代码。

或者,我可以输入两个数组(首选),其中一个被修改。
输出数组不必每次都创建,可以覆盖。 我不能在 C# 中使用不安全的代码,因为这是出于 Assets 目的(兼容性问题)。

模型代码

C#
create arrays aOne,aTwo;
SomeC++Method(aOne,ref aTwo)
or 
aTwo = SomeC++Method(aOne,aTwo)
or 
aTwo = SomeC++Method(aOne) // variant where aTwo is created in C++

SomeC++MethodToDeAllocateMemory() //so there is no memory leak

C++
SomeC++Method(aOne,aTwo)
  for (xyz)
   do someting
  return modified aTwo
SomeC++MethodToDeAllocateMemory()
  release memory

我在这里找到了这个问题的答案: Updating a C# array inside C++ without Marshal.Copy or Unsafe

示例解决方案:

C++代码:

extern "C" __declspec(dllexport) bool PopulateArray(float* floats, int length)
{
    for (int i = 0; i < length; ++i)
    {
        floats[i] = i;
    }

    return true;
}

C#代码

using UnityEngine;
using System.Runtime.InteropServices;

public class Test : MonoBehaviour {

    [DllImport("ArrayFilling", CallingConvention = CallingConvention.Cdecl)]
    public static extern bool PopulateArray([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] float[] floats, int length);

    int length = 4000000; //corresponds to 2000x2000 texture of grayscale values

    void Start()
    {
        //create new array
        float[] floats = new float[length];

        //used for speed test
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

        //check internal method speed
        sw.Start();
        //floats array gets update here
        Populate(ref floats);
        sw.Stop();
        Debug.Log("internal: " + sw.ElapsedMilliseconds);
        sw.Reset();

        //check external method speed
        sw.Start();
        //floats array gets updated here
        PopulateArray(floats, length);
        sw.Stop();
        Debug.Log("external: " +sw.ElapsedMilliseconds);

    }

    void Populate(ref float[] floats)
    {
        for (int i = 0,n = length; i<n;i++)
        {
            floats[i] = i;
        }
    }
}

关于速度:
-外部方法 (DLL) - 5 毫秒
-内部方法(无效填充)- 23 毫秒

最佳答案

.NET 和 C++ 之间的 P/Invoke 支持非常好。通常它相当于使用 [DllImport] 声明一些 extern 方法来告诉运行时在哪里可以找到外部 dll。传递数组时,关键问题是谁在分配它们。如果 C# 代码正在分配它们,那么您通常可以将它们传入 - 例如这里:https://stackoverflow.com/a/5709048/23354 .如果 C++ 代码正在分配它们,那么您的 C# 代码将需要使用指针进行通信,这意味着 unsafe,这是不允许您这样做的(来自问题)。因此,如果可能:让 C# 代码拥有数组。

如果数组没有完全匹配任何可用的调用 - 或者您需要对数组应用偏移量 - 那么您可以声明 extern 方法来获取 IntPtr 或一些 int* 等,但你回到了 unsafe land here。您将使用 fixed 将引用转换为指针,即 fixed(int* ptr = arr) { SomeExternalCall(ptr + offset, arr.Length - offset); }。但如果可能,使用简单的“传递数组”方法会更简单。

请注意,如果方法不是同步的 - 即如果 C++ 代码将保持指针一段时间 - 那么您将需要通过 GCHandle< 手动固定数组。 P/Invoke 代码知道在单个 extern 调用期间固定一个数组(就像上面示例中的 fixed 那样),但它不会保证在调用完成后 不移动它 - 这会使 C++ 代码持有的指针不可靠。 GCHandle 解决了这个问题,方法是在您需要的任何时间段内将对象固定在适当的位置。

不过,坦率地说; C# 在处理数据方面并不差 - 即使使用 100% 安全的代码。如果您的 C++ 实现尚未编写,我建议首先尝试的是:简单地改进 C# 实现以提高效率。

关于c# - 从 C# 到 C++ 的数组再返回,没有不安全的代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48459691/

相关文章:

c# - 无法添加 net.tcp 作为服务引用

c# - 我应该如何在 C# 中设计字符串验证类?

c# - 使用数组中的索引作为数据值?

java - 读取字符数组,一次一个字符

java - 我们可以将 JButton 存储在数组中吗?

c# - java 中 C# System.Windows.Forms.SendKeys.Send 方法的替代方法

c++ - 非静态数据成员初始化

c++ - 在 C++ 中分离流

c++ - 如何使用 Win32 API 对单选框按钮进行分组

android - Json Array Request with volley 不起作用