Сохранение класса на диске при утилизации: есть ли в моем коде ошибки?
Я пытаюсь сделать простой класс, который сериализуется на диск, когда он больше не используется. Код, который у меня есть прямо сейчас (см. Ниже). Код, который у меня сейчас, кажется, работает, но я не полностью уверен в своих знаниях, поэтому мне интересно, есть ли у кого-либо другие серьезные проблемы с этим кодом.
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;
}
}