与C++析构函数相比,C#析构函数和GC并没有真正解决问题

C# destructors and GC not really solving problems compared to C++ destructors

本文关键字:析构函数 并没有 GC 解决问题 C++      更新时间:2023-10-16

EDIT:这个问题是为了比较C#和C++实现。在进行讨论时,没有必要过于夸张。无论如何,我个人更喜欢在C#中开发。

假设我有以下类:

class Foo {
FileStream f;
public Foo() {
f = File.Open("somefile.txt", FileMode.Open);
}
~Foo() {
f.Close();
}
}

然后我在Main中调用以下内容:

static void Main(){
doSomething();
// this one creates an exception, file in use
doSomething();
}
static void doSomething(){
Foo f = new Foo();
}

在C++中,这段代码可以正常工作:

class Foo {
public:
std::ofstream ofs;
Foo() { ofs = ofstream("somefile.txt"); }
~Foo() { ofs.close(); }
};
void doSomething() {
Foo f();
}
int main() {
doSomething();
doSomething();
return 0;
}

当第一个doSomething()超出作用域时,第一个对象调用其析构函数并关闭文件。

在C#中,它总是抛出一个异常,表示该文件正被另一个进程使用。显然,析构函数还没有被调用。

在这里引用MSDN关于析构函数的页面,会让阅读它的人感到困惑,并让他们认为这实际上是一个析构函数。事实上,每当GC决定调用它时,它都是GC调用的终结器,这是我无法控制的。

是的,我可以实现IDisposeable,但我不希望每个使用我的库的程序员都记得调用Dispose(),否则他将出现未处理的异常。

我可以在两个doSomething()方法之间调用GC.Collect(),不会抛出异常。同样,这将类似于Dispose(),库的用户必须记住它。更不用说MS告诉人们请不要使用GC.Collect()的无数警告了!!

所以我的问题是,如果MS想要像C++那样拥有析构函数,那么它们是否只需要在每次方法超出范围时调用垃圾回收器?还是对他们来说会更复杂?这种设计会有什么类型的复杂性?

此外,如果这很简单,他们为什么不这么做呢?

此外,有没有一种方法可以在超出作用域时强制调用C#中的析构函数?

编辑:

我正在研究C++/CLI实现,有趣的是,关键字gcnew正好允许我为C#实现所希望的内容。因此,真正的答案在于这个实现。在他们的文档中,当托管对象超出范围时,GC会被调用,因此托管对象的析构函数也会被调用。并且这不会强制用户手动Dispose对象。

这就回答了这样一个问题:当对象超出范围时,MS让GC类似地工作有多难。它只是自动调用GC.Collect(),这迫使GC调用类似于C++中发生的析构函数。

正如您所推测的,C#没有确定性析构函数,将其与C++进行比较总是"苹果对桔子"。此外,在C#类上实现析构函数将导致该类实例的GC清理的确定性更低,因为GC将把任何带有析构函数的实例放入列表中,以便稍后由单独的线程处理。

如果希望对实例进行确定性处理,请实现IDisposable接口/模式。这就是它的作用。

这不是C#的问题。你的课应该正确地处理开场和结束。

public static class Foo
{
public static void DoSomething()
{
string someText = "someText";
using (StreamWriter writer = new StreamWriter("myfile.txt"))
{
writer.Write(someText);
writer.Close();
writer.Dispose();
}
}
}
相关文章: