c# - PINVOKE 内存管理的单元测试
问题描述
我有一个使用一些非托管 C++ 代码的 C# 应用程序(.net core 3.1)。互操作是使用 PINVOKE 实现的。
我的代码做这样的事情(简化):
double[] managedArray = new[] { 1.0, 2.0, 3.0 };
// This is the first PINVOKE call
// The unmanaged object is expected to copy the array internally.
UnmanagedObject unmanaged = CreateUnmanagedObject(managedArray);
[...]
// This is another PINVOKE call which uses the data passed in to the constructor.
// This call can also happen in a different method.
// At this point managedArray could already have been garbage collected.
unmanaged.DoStuff();
我遵循这种模式,即 C++ 代码应先在内部复制数组,然后再将其存储为 UnmanagedObject 状态。
现在我想为命题写一个单元测试非托管对象应该在内部复制数组。 我的尝试如下所示:
UnmanagedObject unmanaged;
{
double[] managedArray = new[] { 1.0, 2.0, 3.0 };
unmanaged = CreateUnmanagedObject(managedArray);
}
// With this call I'm relying on the fact, that the GC will collect
// managedArray, because it's gone out of scope.
System.GC.Collect();
// If DoStuff works properly here, it means that the C++ code
// has copied the array internally.
unmanaged.DoStuff();
GC 将收集managedArray
的短语是否正确,因为它超出了范围?这保证总是发生吗?
[我通过删除一些不相关的内容来编辑问题以使其更加清晰。有些评论不再适用。]
解决方案
您不需要在将原始类型数组传递给 pinvoke 之前对其进行固定/修复。您可以直接传递它们,.NET 将在方法调用期间为您固定它们(因此当 pinvoke 返回时它们将被取消固定)
[DllImport("somedll.dll")]
private static extern UnmanagedObject CreateUnmanagedObject(double[] array, int lenArray);
如果您想检查您的数据是否真的被 C++ 内部复制,您可以:
UnmanagedObject unmanaged;
{
double[] managedArray = new[] { 1.0, 2.0, 3.0 };
unmanaged = CreateUnmanagedObject(managedArray);
for (int i = 0; i < managedArray.Length; i++)
{
managedArray[i] = double.NaN;
}
}
// At this point the GC should collect the managedArray, if I trigger it.
System.GC.Collect();
// If DoStuff works properly here, it means that the C++ code
// has copied the array internally
unmanaged.DoStuff();
因此,您可以将数组的值设置为其他值。
你甚至可以检查对象是否真的被收集了:
WeakReference wr;
{
double[] managedArray = new[] { 1.0, 2.0, 3.0 };
wr = new WeakReference(managedArray);
for (int i = 0; i < managedArray.Length; i++)
{
managedArray[i] = double.NaN;
}
}
// At this point the GC should collect the managedArray, if I trigger it.
System.GC.Collect();
if (wr.IsAlive)
{
Console.WriteLine("Warning: the object hasn't been collected");
}
else
{
Console.WriteLine("The object has been collected");
}
请注意,如果程序已在 Debug 模式下编译,则 GC 直到方法结束时才会收集对象,而在 Release 模式下,可以在范围结束时收集对象。
推荐阅读
- javascript - 创建标签时运行js函数
- ionic3 - 如何在 Angular 6 中添加 ng2-smart-table 嵌套列
- python - 删除和移动熊猫 df 中的值
- file - 寻找在 docker 上运行的两个 Web 应用程序之间存储文件的最佳方式
- google-app-maker - AppMaker:每个部署的环境变量?获取部署名称?
- vuejs2 - vue2-google-maps 在 vue.js 中添加自定义标记
- appium-ios - 在 Bitrise for Appium iOS 测试中获取元素的可见性为 false
- abap - 如何在网格上制作表格大小,如 ABAP 中的容器大小
- c# - Xamarin Forms - 根据原始源文件大小在 C# 中设置图像大小
- linux - 如何列出加载到内核中的所有 bpf 程序?(例如 tc-bpf)