C#基本程序中的析构函数不起作用(输出丢失) [英] Destructor in C# basic program does not work (output missing)

查看:224
本文介绍了C#基本程序中的析构函数不起作用(输出丢失)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经在下面编写了非常基本的程序,我是C#的新手。不会调用析构函数〜Program(),因此在输出中看不到被调用的析构函数字符串。我已经检查了其他类似的问题,但找不到我的答案。

I have written the very basic program below, I am new to C#. The destructor ~Program() doesn't get called, so I do not see in the output the 'Destructor called' string. I have checked other similar questions but I don't find the answer to mine. Thanks.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using static System.Console;

namespace LyfeCicleObject
{
    class Program
    {
        public Program()
        {
            WriteLine("Cons called");
        }

        ~Program()
        {
            WriteLine("Destructor called");           
        }

        static void Main(string[] args)
        {
            WriteLine("Main started");

            Program p1 = new Program();
            {
                WriteLine("Block started");
                Program p2 = new Program();
                WriteLine("Block ended");
            }

            WriteLine("Main ended");
        }

        }

    }


推荐答案

简短的答案-您未看到析构函数调用输出的原因-埋在了注释中:

The short answer -- the reason you're not seeing the "Destructor called" output -- was buried somewhere in the comments:


.NET Core在程序结束时不运行终结器

(请参阅:定型器(C#编程指南))。

.NET Framework会尝试 来实现,但.NET Core却不会

.NET Framework will attempt to do it but .NET Core just won't do it.

免责声明:我们无法知道这些陈述是否将继续适用;

Disclaimer: We have no way of knowing if these statements will continue to hold true; this is how they're implemented and documented as of now.

根据Raymond Chen的说法,不过,在他的帖子每个人都以错误的方式考虑垃圾回收,如果则无效。 NET Framework也没有在程序末尾运行终结器。相关的引语从不​​同的角度说:

According to Raymond Chen, though, in his post Everybody thinks about garbage collection the wrong way, it would not be invalid if .NET Framework didn't run finalizers at the end of the program, either. The relevant quote, which says it from a different perspective, is this:


正确编写的程序不能假设终结器会运行。

A correctly-written program cannot assume that finalizers will ever run.

只要您不认为终结器会运行,那么它们的实现方式或

So as long as you don't assume that finalizers will run, it shouldn't matter how they're implemented or if an implementation changes.

在继续使用C#之前,您必须放弃.NET中的析构函数的想法,因为它们只是不存在。 C#将C ++的析构函数 syntax 用于终结器,但相似之处就止于此。

Before going any further with C#, you're going to have to abandon the idea of destructors in .NET because they simply don't exist. C# uses C++'s destructor syntax for finalizers but the similarities stop there.

好消息是一种与您尝试做的事情接近的方法,但是这需要进行范式转换,这是您对资源获取和释放的看法的重大变化。

The good news is that there is a way to do something close to what you were trying to do, but it takes a paradigm shift, a substantial change in how you think about resource acquisition and release. Whether or not you really need to do it is a totally different question.

终结器不是释放需要释放资源的唯一方法,甚至不是最佳方法。及时发布。

Finalizers aren't the only way, or even the best way, to release resources that need to be released in a timely manner. We have the disposable pattern to help with that.

一次性模式允许类实现者选择确定性地释放资源(不包括托管堆上的内存)的通用机制。 )。它包括终结器,但只有在对象处置不当(尤其是过程没有终止)的情况下,才有最后一次清除机会。

The disposable pattern allows class implementors to opt in to a common mechanism for deterministically releasing resources (not including memory on the managed heap). It includes finalizers but only as a last chance at cleanup if the object wasn't disposed properly, especially if the process isn't terminating.

我要说与C ++析构函数相比,您将看到的差异是:

I'd say the main differences you'll see compared to C++ destructors are:


  1. 该类的实现者还必须支持一次性模式。

  2. 该类的消费者还必须使用 using 语句选择加入。

  1. There's more that the class's implementor must do support the disposable pattern.
  2. The class's consumer also has to opt in to it with a using statement.

您不会看到的是不必立即回收内存。

What you won't see is that the memory won't necessarily be reclaimed immediately.

如果您想了解更多有关如何操作的信息,请继续阅读...

在进入任何代码之前,值得一提注意:

Before I get into any code, it's worth mentioning some points of caution:


  • 终结器永远不能为空。

  • 正如mjwills在评论中指出的那样,在99.9%的时间内,您不应该编写终结器。如果发现自己编写了一个,请退后一步,确保有充分的理由就.NET代码进行操作,而不是因为您会在C ++中这样做。

  • 通常,从实现了一次性模式的类派生出来之后,您将覆盖 Dispose(bool),而不是创建需要继承的类层次结构的基础是一次性的。例如,Windows Forms应用程序中的 .Designer.cs 文件会覆盖 Dispose(bool)以便处理 components 字段,如果它不是

  • A finalizer should never be empty. It causes instances to be kept alive longer and for nothing.
  • As mjwills stated in a comment, 99.9% of the time, you shouldn't be writing a finalizer. If you find yourself writing one, take a step back and make sure you have a good reason to do it in terms of .NET code and not because you would do it that way in C++.
  • More often than not, you'll be overriding Dispose(bool) after deriving from a class that implements the disposable pattern rather than creating the base of a class hierarchy that needs to be disposable. For example, the .Designer.cs files in Windows Forms applications override Dispose(bool) in order to dispose of the components field if it's not null.

好的,代码...

以下是实现一次性模式的简单类的示例。它不提供对子类的支持,因此标记为密封,而 Dispose(bool)私有

The following is an example of a simple class that implements the disposable pattern. It doesn't provide support for child classes so it's marked sealed and Dispose(bool) is private.

public sealed class SimpleDisposable : IDisposable
{
    public SimpleDisposable()
    {
        // acquire resources
    }

    ~SimpleDisposable()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        // Suppress calling the finalizer because resources will have been released by then.
        GC.SuppressFinalize(this);
        Dispose(true);
    }

    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            // release managed resources
            // (you don't want to do this when calling from the finalizer because the GC may have already finalized and collected them)
        }

        // release unmanaged resources
    }
}

实际清理发生在 Dispose(bool )方法。如果参数为 true ,则表示通过 IDisposable 接口(通常为使用语句(但不一定)),也可以清理托管资源。如果它是 false ,则意味着处置是作为GC扫描的一部分进行的,因此您不能触摸托管资源,因为它们可能已被收集。

Actual cleanup takes place in the Dispose(bool) method. If the parameter is true, it means that disposal is happening via the IDisposable interface (usually a using statement but not necessarily) and it's okay to clean up managed resources as well. If it's false, it means that disposal is happening as part of a GC sweep, so you can't touch managed resources because they may have already been collected.

如果您要编写一个需要支持一次性模式的基类,则情况会稍有变化: Dispose(bool)变为受保护的虚拟的,因此它可以被子类覆盖,但仍对消费者不可用。

If you're writing a base class that needs to support the disposable pattern, things change slightly: Dispose(bool) is becomes protected and virtual so it can be overridden by subclasses but is still inaccessible to the consumer.

以下是支持子类的一次性模式的基类示例。

The following is an example of a base class that supports the disposable pattern for subclasses.

public abstract class BaseDisposable : IDisposable
{
    protected BaseDisposable()
    {
        // acquire resources
    }

    ~BaseDisposable()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        GC.SuppressFinalize(this);
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // release managed resources
        }

        // release unmanaged resoures
    }
}

然后是使用该支持的子类。子类也不需要实现终结器或 IDisposable.Dispose 。他们要做的就是重写 Dispose(bool),处置自己的资源,然后调用基本实现。

And then the following is a subclass that uses that support. Subclasses don't also need to implement the finalizer or IDisposable.Dispose. All they have to do is override Dispose(bool), dispose of their own resources, and then call the base implementation.

public class DerivedDisposable : BaseDisposable
{
    public DerivedDisposable()
    {
        // acquire resources for DerivedDisposable
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // release DerivedDisposable's managed resources
        }

        // release DerivedDisposable's unmanaged resources

        // Let the base class do its thing
        base.Dispose(disposing);
    }
}

那么处置托管和非托管资源是什么意思?

So what does it mean to dispose managed and unmanaged resources?

受管资源就像其他一次性对象,甚至是非一次性对象(例如字符串)一样。 BCL中的某些一次性类型会将此类字段设置为 null ,以确保GC找不到对它们的有效引用。

Managed resources are things like other disposable objects and even non-disposable objects (e.g. strings). Some disposable types in the BCL will set such fields to null to ensure that the GC doesn't find active references to them.

在处置您的班级时,消费者决定不再需要它及其资源。如果您的对象包含其他一次性对象,则可以将这些对象放置在链上,这样下去,因为在垃圾回收期间不会发生这种情况。

When your class is being disposed, the consumer has decided that it and its resources are no longer needed. If your object contains other disposables, it's okay to dispose those objects, and so on down the chain, because it's not happening during garbage collection.

非托管资源就像文件一样句柄,全局内存,内核对象...通过调用操作系统分配的几乎所有内容。这些不受垃圾收集器的影响,无论如何都需要释放,因此它们不受处置测试的约束。

Unmanaged resources are things like file handles, global memory, kernel objects... pretty much anything you allocated by making a call to the operating system. Those aren't affected by the garbage collector and need to be released no matter what, so they're not subject to the disposing test.

如果您的一次性对象使用了另一个具有非托管资源的一次性对象,则该对象有责任实施Disposable模式以释放其资源,并由您负责使用。

If your disposable object used another disposable object that has an unmanaged resource, it's that object's responsibility to implement the Disposable pattern to release its resources, and your responsibility to use it.

并非所有实现 IDisposable 的对象实际上都具有非托管资源。通常,基类仅会支持一次性模式,因为它的作者知道,至少派生自该类的类可能需要使用非托管资源。但是,如果某个类未实现可抛弃型模式,则其子类之一可以在需要时引入该支持。

Not all objects that implement IDisposable actually have unmanaged resources. Often a base class will support the disposable pattern simply because its author knows that at least one class that derives from it might need to use unmanaged resources. However, if a class doesn't implement the disposable pattern, one of its subclasses can introduce that support if it needs it.

让我们稍微修改一下程序,使其达到预期的效果,但是现在使用一次性模式。

Let's alter your program a bit and make it do what you were expecting, but using the disposable pattern now.

注意:据我所知, Main 创建包含 Program 类的实例的情况并不常见。我在这里这样做是为了使内容尽可能接近原始内容。

Note: As far as I know, it's not very common to have Main create an instance of the containing Program class. I'm doing it here to keep things as close to the original as possible.

using System;

internal sealed class Program : IDisposable
{
    private readonly string _instanceName;

    public Program(string instanceName)
    {
        _instanceName = instanceName;

        Console.WriteLine($"Initializing the '{_instanceName}' instance");
    }

    ~Program()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        GC.SuppressFinalize(this);
        Dispose(true);
    }

    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            Console.WriteLine($"Releasing the '{_instanceName}' instance's managed resources");
        }

        Console.WriteLine($"Releasing the '{_instanceName}' instance's unmanaged resources");
    }

    private static void Main(string[] args)
    {
        Console.WriteLine("Main started");

        Program p0 = new Program(nameof(p0));
        using (Program p1 = new Program(nameof(p1)))
        {
            Console.WriteLine("Outer using block started");
            using (Program p2 = new Program(nameof(p2)))
            {
                Console.WriteLine("Inner using block started");
                Console.WriteLine("Inner using block ended");
            }
            Console.WriteLine("Outer using block ended");
        }

        Console.WriteLine("Main ended");
    }
}

构建并运行.NET Framework 4.7.2和您将得到以下输出:

Build and run against .NET Framework 4.7.2 and you get the following output:

Main started
Initializing the 'p0' instance
Initializing the 'p1' instance
Outer using block started
Initializing the 'p2' instance
Inner using block started
Inner using block ended
Releasing the 'p2' instance's managed resources
Releasing the 'p2' instance's unmanaged resources
Outer using block ended
Releasing the 'p1' instance's managed resources
Releasing the 'p1' instance's unmanaged resources
Main ended
Releasing the 'p0' instance's unmanaged resources

构建并运行.NET Core 2.1,您将获得以下输出:

Build and run against .NET Core 2.1 and you get the following output:

Main started
Initializing the 'p0' instance
Initializing the 'p1' instance
Outer using block started
Initializing the 'p2' instance
Inner using block started
Inner using block ended
Releasing the 'p2' instance's managed resources
Releasing the 'p2' instance's unmanaged resources
Outer using block ended
Releasing the 'p1' instance's managed resources
Releasing the 'p1' instance's unmanaged resources
Main ended

实例 p1 p2 被处置由于使用 using 语句,它们以相反的顺序构造,并且托管和非托管资源都被释放。这是尝试使用析构函数背后的预期行为。

Instances p1 and p2 were disposed in the reverse order in which they were constructed because of the using statements, and both managed and unmanaged resources were released. This was the desired behavior behind trying to use a "destructor".

另一方面,.NET Framework和.NET Core最终却有所不同,显示了我在一开始提到的差异:

On the other hand, .NET Framework and .NET Core did things a bit different at the end, showing the differences I mentioned at the beginning:


  • .NET Framework的GC称为 p0 ,所以它只释放了非托管资源。

  • .NET Core的GC没有调用 p0 的终结器。

  • .NET Framework's GC called the finalizer for p0 so it only released the unmanaged resources.
  • .NET Core's GC did not call the finalizer for p0.

这篇关于C#基本程序中的析构函数不起作用(输出丢失)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆