C# C+中的早期终结和内存泄漏+/CLI库

C# C+中的早期终结和内存泄漏+/CLI库,c#,c++-cli,resource-management,C#,C++ Cli,Resource Management,在我正在进行的一个C++/CLI(和C#)项目中,似乎很早就调用了终结器,我遇到了一些问题。这似乎是一个非常复杂的问题,我将从代码中提到许多不同的类和类型。幸运的是,它是开源的,您可以在这里遵循:(mercurial repository)我还尝试在适当的地方直接链接到文件浏览器,这样您可以在阅读时查看代码。我们处理的大部分代码都在存储库的pstsdk.mcpp文件夹中 现在的代码处于一种相当可怕的状态(我正在处理),我正在处理的代码的当前版本位于Finalization fixes(不稳定!)

在我正在进行的一个C++/CLI(和C#)项目中,似乎很早就调用了终结器,我遇到了一些问题。这似乎是一个非常复杂的问题,我将从代码中提到许多不同的类和类型。幸运的是,它是开源的,您可以在这里遵循:(mercurial repository)我还尝试在适当的地方直接链接到文件浏览器,这样您可以在阅读时查看代码。我们处理的大部分代码都在存储库的
pstsdk.mcpp
文件夹中

现在的代码处于一种相当可怕的状态(我正在处理),我正在处理的代码的当前版本位于
Finalization fixes(不稳定!)
分支中。该分支中有两个变更集,为了理解我冗长的问题,我们需要同时处理这两个变更集。(变更集:和)

某个背景下,本项目是C++编写的C++/CLI包装器。我不是这个项目的协调人,有几个设计决定我不同意,因为我相信你们中的许多人都会看代码,但我离题了。我们在C++/CLI dll中封装了原始库的许多层,但在C#dll中公开了易于使用的API。之所以这样做,是因为该项目的目的是将整个库转换为托管C#代码

如果您能够获得要编译的代码,则可以使用来重现问题


问题 最新的变更集名为,显示了我遇到的原始问题。此代码中的每个类都使用相同的模式释放非托管资源。下面是一个示例(C++/CLI):

DBContext::~DBContext()
{
此->!DBContext();
GC::SuppressFinalize(此);
}
DBContext::!DBContext()
{
如果(pst.get()!=nullptr)
_pst.reset();//pst是一个clr范围的ptr(托管类型)
//它包装了一个共享的ptr。
}
此代码有两个好处。首先,当像这样的类位于
using
语句中时,会立即正确地释放资源。其次,如果用户忘记了dispose,当GC最终决定完成该类时,非托管资源将被释放

这种方法的问题是,我根本无法理解,有时GC会决定完成一些用于枚举文件中数据的类。这种情况发生在许多不同的PST文件中,我已经确定它与正在调用的Finalize方法有关,即使该类仍在使用中

我可以始终让它发生在1。提前调用的终结器位于文件中的
nodeicollection
类中。如果您能够运行链接到上面的代码(由于依赖于boost库,因此很难设置此项目),则应用程序将失败并出现异常,因为
\u节点
列表设置为null,并且由于终结器运行而重置了
\u db
指针

1)
NodeIdCollection
类中的枚举代码是否存在任何明显的问题,这些问题会导致GC在该类仍在使用时完成该类? 我只能通过下面描述的解决方法使代码正常运行


难看的变通办法 现在,我可以通过将所有资源管理代码从每个终结器(
!classname
)移动到析构函数(
~classname
)来解决这个问题。这解决了这个问题,尽管它并没有解决我的好奇心,为什么课程会提前结束

然而,这种方法有一个问题,我承认这更像是设计的问题。由于在代码中大量使用指针,几乎每个类都处理自己的资源,并要求处理每个类。这使得使用枚举非常难看(C#):

foreach(pst.Messages中的var msg)
{
//如果删除这个using语句,我们将
//内存泄漏
使用(味精)
{
//代码在这里
}
}
作用于集合中项目的using语句对我来说是错误的,然而,使用这种方法来防止任何内存泄漏是非常必要的。没有它,永远不会调用dispose,也永远不会释放内存,即使调用了pst类上的dispose方法也是如此

我很想改变这个设计。在编写这段代码时,除了我对C++/CLI知之甚少甚至一无所知之外,最根本的问题是我不能将本机类放在托管类中。我觉得可以使用作用域指针,在类不再使用时自动释放内存,但我不能确定这是否是一种有效的方法,或者它是否可以工作。所以,我的第二个问题是:

2) 以轻松的方式处理托管类中的非托管资源的最佳方法是什么? 为了详细说明,我是否可以用最近添加到代码中的
clr\u scoped\u ptr
包装器替换本机指针(来自stackexchange问题)。或者我需要将本机指针包装成
范围内的ptr
智能ptr


谢谢你阅读所有这些,我知道这是很多。我希望我已经足够清楚了,这样我可以从比我更有经验的人那里得到一些见解。这是一个如此大的问题,我打算在允许的情况下增加一笔赏金。希望有人能帮忙

谢谢



1该文件是免费提供的PST文件的一部分

刷新我对析构函数/最终分析器的记忆,我认为,从某些方面来说,您至少可以简化一点代码

以下是我对您的序列的版本:

DBContext::~DBContext()
{
    this->!DBContext();
}

DBContext::!DBContext()
{
    delete _pst;
    _pst = NULL;
}
“GC:”