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

时间:2017-02-05 00:29:03

标签: c# c++ garbage-collection c++-cli destructor

编辑:此问题用于比较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#中,这将始终抛出一个异常,说明该文件正由另一个进程使用。显然析构函数没有被调用。

参考有关析构函数here的MSDN页面,它会让任何人阅读它并使他们认为这实际上是一个析构函数。虽然它实际上是GC在GC决定调用它时调用的终结器,但这不在我的手中。

是的,我可以实现IDisposeable但我不希望每个使用我的库的程序员记住调用Dispose(),否则他将有未处理的例外。

我可以在两个GC.Collect()方法之间调用doSomething(),不会抛出任何异常。再次,这将类似于Dispose(),并且图书馆的用户必须记住它。没有提到MS告诉人们请不要使用的绝对警告GC.Collect()

所以我的问题是,如果MS希望像C ++那样有析构函数,那么每次方法超出范围时,他们是否只需要调用垃圾收集器?或者对他们来说会更复杂吗?这种设计会出现什么类型的并发症?

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

此外,还有一种方法可以在超出范围时强制调用C#中的析构函数吗?

编辑:

我正在研究C ++ / CLI实现,有趣的是关键字gcnew允许我想要为C#实现的目标。所以答案真的在于实施。与在他们的文档中一样,当托管对象超出范围时,将调用GC,因此将调用托管对象的析构函数。并且这不会强制用户手动Dispose该对象。

这回答了一个问题,即当一个对象超出范围时,MS如何“难以”使GC同样工作。它只是自动调用GC.Collect(),它强制GC调用析构函数,类似于C ++中发生的情况。

2 个答案:

答案 0 :(得分:3)

正如你所推测的那样,C#没有确定性的析构函数,将它与C ++进行比较总是“苹果到橙子”。此外,在C#类上实现析构函数将导致GC对该类实例的清理甚至更不确定,因为GC会将带有析构函数的任何实例放入列表中,以便稍后由单独的线程处理。

如果您想要确定性地处理实例,请实现IDisposable接口/模式。这就是它的用途。

答案 1 :(得分:0)

这不是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();
        }
    }
}