Сохранение класса на диске при утилизации: есть ли в моем коде ошибки?

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

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

~MyClass()
{
    Dispose(false);
}

protected virtual void Dispose(bool disposing)
{
    if (!this.disposed)
    {
        MemoryStream ms = new MemoryStream();
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(ms, this);
        byte[] output = Dostuff(ms);
        File.WriteAllBytes(DBPATH, output);
    }
    this.disposed = true;
}

Ответ 1

Практически невозможно написать финализатор правильно, и выполнение такого рода работ в одном - это просто рецепт катастрофы. Не говоря уже о том, что это убьет производительность и будет невозможно отладить. Правило 1 для финализаторов никогда не использует их. Правило 2 (только для продвинутых пользователей) не использует их, если вы действительно не уверены, что вам нужно.

Если это просто для забавного хобби-проекта, тогда нет никакого реального вреда, и он, вероятно, будет работать достаточно хорошо, но я бы заплакал, если бы увидел когда-нибудь подобное в производственной кодовой базе.

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

class MyClass
{
    private bool dirty; // set this whenever the object changes

    ~MyClass 
    {
        if (this.dirty) 
        {
            Debug.Fail("Object was not saved.");
        }
    }

    public void Save()
    {
        if (this.dirty)
        {
            // TODO: do the save
            this.dirty = false;
        }
    }
}

Ответ 2

Это, скорее всего, сработает, но я бы этого не сделал. Делая это, вы потенциально выполняете потенциально опасный код в потоке финализации. Если что-то пойдет не так, вы окажетесь в плохом состоянии....

Dispose должен действительно ничего не делать, кроме как избавляться от ваших ресурсов. Я бы настоятельно рекомендовал перевести это на другой метод и сделать его частью API-интерфейса объекта, вместо того, чтобы полагаться на IDisposable для обработки обработки для вас.

Ответ 3

Прежде всего, я считаю, что у вас довольно слабый дизайн, потому что ваш класс нарушает принцип единой ответственности. Гораздо более предпочтительным выделяются две обязанности: сериализуемая сущность и сохранение/чтение этого объекта в/из постоянного хранилища. В большинстве случаев сериализуемые объекты являются lightwait и finalizable классы не являются.

Во-вторых, вы должны избегать сложной логики внутри своих финализаторов. Например, было бы намного лучше сохранить сериализуемый класс в постоянное хранилище в методе Storage.Dispose. И из метода finalizer только записывать предупреждение в файл журнала, потому что он показывает неприемлемый класс Использование хранилища:

[Serializable]
public class MySerializableClass {
}

public sealed class MyStorage : IDisposable {

  ~MyStorage()
  {
     Dispose(false);
  }


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


  void Dispose(bool disposing)
  {
     if (!this.disposed)
     {
        if (disposing)
        {
          //We can access to all managed resources
          using (var ms = new MemoryStream())
          {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(ms, mySerializableClass);
            byte[] output = Dostuff(ms);
            File.WriteAllBytes(DBPATH, output);
          }
        }
        else
        {
           //Inappropriate storage usage!
           //We can't guarantee that mySerializableClass object would
           //properly saved to persistant storage.
           //Write warning to log-file. We should fix our code
           //and add appropriate usage!
        }
    }
    this.disposed = true;
 }

}