与C++析构函数相比,C#析构函数和GC并没有真正解决问题
C# destructors and GC not really solving problems compared to C++ destructors
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();
}
}
}
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 重载 -> shared_ptr 个实例中的箭头运算符<interface>,接口中没有纯虚拟析构函数
- 在没有动态内存的世界中,我是否需要虚拟析构函数?
- 如何正确定义C++类析构函数并将其链接到主文件?
- 有没有办法保证析构函数的相对顺序?
- 如何从类成员函数返回指针,例如 size_t * class :: function(); 并使用类析构函数 ~size
- 是否可以访问类数据成员并在析构函数中对它们执行操作?
- 当我使用dynamic_cast并删除对象删除时,析构函数是如何工作的?
- 使用 std::function 作为成员函数,它捕获"this",并在析构函数之后从复制的 lam
- new[] / delete[] 并在C++中抛出构造函数/析构函数
- 为什么没有捕获我来自析构函数的异常
- 为什么在将多态行为与指向接口的指针一起使用时没有调用析构函数?
- 有没有办法让shared_ptr.reset(new obj)首先调用析构函数?
- 与C++析构函数相比,C#析构函数和GC并没有真正解决问题
- 当声明了虚拟析构函数但没有实现时会发生什么情况
- 通过复制将对象传递给 CUDA 内核会调用其析构函数并过早释放内存
- 是否可以使用 std::shared_ptr 创建共享对象池,并在没有自定义析构函数的情况下创建weak_ptr
- push_back(*obj) 正在调用析构函数并导致SDL_BlitSurface崩溃
- 当点击析构函数并使用:memory:时,SQLite SQLite_BUSY[5]
- 手动调用析构函数并重用内存