问题描述
我有一个 C# 应用程序(.net core 3.1),它使用了一些非托管 C++ 代码。互操作是使用 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,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,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模式下,对象可以在作用域结束时收集。