Деструктор в базовой программе C # не работает (вывод отсутствует) - PullRequest
0 голосов
/ 23 января 2019

Я написал очень простую программу ниже, я новичок в C #. Деструктор ~ Program () не вызывается, поэтому я не вижу в выходных данных строки с именем Destructor. Я проверил другие подобные вопросы, но не нашел ответа на свой. Спасибо.

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");
        }

        }

    }

Ответы [ 2 ]

0 голосов
/ 25 января 2019

Краткий ответ - причина, по которой вы не видите вывод "Destructor звонил" - была похоронена где-то в комментариях:

.NET Core не запускает финализаторы в конце программы

(См .: Финализаторы (Руководство по программированию в C #) ).

.NET Framework попытается сделать это, но .NET Core просто не сделает этого.

Отказ от ответственности : У нас нет никакой возможности узнать, будут ли эти заявления продолжать выполняться; вот как они реализованы и документированы на данный момент.

Однако, по словам Рэймонда Чена, в своем посте Все думают о сборе мусора неправильно , он не будет недействительным, если .NET Framework не запускает финализаторы в конце программы, либо , Соответствующая цитата, которая говорит это с другой точки зрения, такова:

Правильно написанная программа не может предполагать, что финализаторы когда-либо будут работать.

Так что, пока вы не предполагаете, что финализаторы будут работать, не должно иметь значения, как они реализованы, или если реализация изменится.

Прежде чем идти дальше с C #, вам придется отказаться от идеи деструкторов в .NET , потому что их просто не существует. C # использует деструктор C ++ синтаксис для финализаторов, но сходство на этом заканчивается.

Хорошей новостью является то, что - это способ сделать что-то близкое к тому, что вы пытались сделать, но это требует смены парадигмы, существенного изменения в том, как вы думаете о приобретении и выпуске ресурсов. Нужно ли вам это делать - это совершенно другой вопрос.

Финализаторы - это не единственный или даже не лучший способ высвободить ресурсы, которые должны быть выпущены своевременно. У нас есть одноразовый шаблон, чтобы помочь с этим.

Одноразовый шаблон позволяет разработчикам классов выбрать общий механизм для детерминированного освобождения ресурсов (не включая память в управляемой куче). Он включает финализаторы, но только в качестве последнего шанса на очистку, если объект не был правильно расположен, особенно если процесс не завершается.

Я бы сказал, что основные отличия, которые вы увидите по сравнению с деструкторами C ++:

  1. Более того, разработчик класса должен поддерживать одноразовый шаблон.
  2. Потребитель класса также должен включить в него оператор using.

Чего вы не увидите, так это того, что память не обязательно будет немедленно восстановлена.


Если вы хотите узнать больше о том, как, читайте дальше ...

Прежде чем приступить к какому-либо коду, стоит упомянуть некоторые предостережения:

  • Финализатор никогда не должен быть пустым. Это приводит к тому, что экземпляры остаются живыми дольше и даром.
  • Как говорится в комментарии mjwills, в 99,9% случаев вам не следует писать финализатор. Если вы обнаружите, что пишете его, сделайте шаг назад и убедитесь, что у вас есть веская причина сделать это с точки зрения кода .NET, а не потому, что вы сделали бы это таким образом в C ++.
  • Чаще всего вы будете переопределять Dispose(bool) после получения из класса, который реализует шаблон одноразового использования, а не создания основы иерархии классов, которая должна быть одноразовой. Например, файлы .Designer.cs в приложениях Windows Forms переопределяют Dispose(bool), чтобы удалить поле components, если оно не null.

Хорошо, код ...

Ниже приведен пример простого класса, который реализует одноразовый шаблон. Он не обеспечивает поддержку дочерних классов, поэтому он помечен sealed, а Dispose(bool) равен 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 (обычно оператор using, но не обязательно), и также можно очистить управляемые ресурсы.Если это false, это означает, что удаление происходит как часть очистки GC, поэтому вы не можете коснуться управляемых ресурсов, потому что они, возможно, уже были собраны.

Если вы пишете базовый класс, которыйнеобходимо поддерживать одноразовый шаблон, все меняется незначительно: Dispose(bool) становится protected и virtual, поэтому он может быть переопределен подклассами, но все еще недоступен для потребителя.

Ниже приведен примербазовый класс, который поддерживает одноразовый шаблон для подклассов.

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), избавиться от своих собственных ресурсов и затем вызвать базовую реализацию.

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);
    }
}

Так что же означает распоряжаться управляемыми и неуправляемыми ресурсами?

Управляемыми ресурсами являются такие вещи, как другие одноразовые объекты и даже не одноразовые объекты (например, строки).Некоторые одноразовые типы в BCL устанавливают такие поля в null, чтобы гарантировать, что GC не найдет активные ссылки на них.

Когда ваш класс утилизируется, потребитель решил, что он и его ресурсыбольше не нужны.Если ваш объект содержит другие одноразовые объекты, то все в порядке, расположив их по цепочке, и т. Д. По цепочке, потому что это не происходит во время сборки мусора.

Неуправляемые ресурсы - это такие вещи, как дескрипторы файлов, глобальная память, объекты ядра.почти все, что вы выделили, позвонив в операционную систему.На них не влияет сборщик мусора, и они должны быть освобождены, несмотря ни на что, поэтому они не подлежат тесту disposing.

Если ваш одноразовый объект использовал другой одноразовый объект, который имеет неуправляемый ресурсответственность за реализацию шаблона Disposable для освобождения своих ресурсов лежит на этом объекте, а ответственность за его использование лежит на вас.

Не все объекты, реализующие IDisposable, на самом деле имеют неуправляемые ресурсы.Часто базовый класс поддерживает шаблон одноразового использования просто потому, что его автору известно, что по крайней мере один класс, производный от него, может нуждаться в использовании неуправляемых ресурсов.Однако, если класс не реализует одноразовый шаблон, один из его подклассов может представить эту поддержку, если он в ней нуждается.


Давайте немного изменим вашу программу и заставим ее делать то, что вы ожидали,но используя одноразовый шаблон сейчас.

Примечание: Насколько я знаю, Main не очень часто создает экземпляр содержащего класса Program.Я делаю это здесь, чтобы все было как можно ближе к оригиналу.

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, и вы получите следующий вывод:

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, и вы получите следующий вывод:

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, а также управляемые и неуправляемые ресурсы были освобождены.Это было желаемое поведение при попытке использовать «деструктор».

С другой стороны, .NET Framework и .NET Core делали вещи немного по-другому, показывая различия, о которых я упоминал в начале:

  • GC .NET Framework называет финализатор для p0, поэтому он освобождает только неуправляемые ресурсы.
  • GC .NET Core не вызывает финализатор для p0.
0 голосов
/ 23 января 2019

Если вы хотите увидеть вывод из финализатора:

  • добавить новый метод
  • инициализировать переменную p1 внутри
  • добавить GC.Collect ()
  • вызов нового метода из Main
  • 1012 * выгода *

Если вы хотите узнать причину текущего поведения: переменная p1 существует в области действия метода Main и будет собираться GC, когда область станет недоступной, и я предполагаю, что это происходит, когда программа уже остановила означает отсутствие GC (память будет освобождена другим способом).

...