classe base IDisposable que detém recurso gerenciado descartável, o que fazer em subclasses?
-
06-07-2019 - |
Pergunta
Eu tenho uma classe base que possui um recurso descartável gerenciado (.NET PerformanceCounter). Eu entendo sobre a implementação de IDisposable na classe para que eu possa chamar explicitamente Dispose sobre o recurso. A partir dos exemplos que vi, as pessoas normalmente usam uma variável privada membro boolean "dispostos" e defini-lo como true dentro de Descarte. Mais tarde, se houver uma tentativa de acessar um método pública ou a propriedade, uma ObjectDisposedException é gerado se "disposto" é verdadeira.
E quanto nas subclasses? Como é que as subclasses, em seus métodos públicos e propriedades, sabemos que que tinham sido eliminados? No começo eu pensei que as subclasses não teria que nada de especial (como implementar sua própria versão de Descarte) desde a coisa que precisa ser eliminado apenas na classe base (vamos supor que as subclasses não estará adicionando quaisquer dados que necessita de ser explicitamente disposta) e a classe base Descarte deve lidar com isso. Caso as subclasses substituir a classe base método Dispose virtual unicamente com a finalidade de definir a sua própria variável de membro 'descartados'?
Aqui está uma muito despojada versão da hierarquia de classes em questão.
class BaseCounter : IBaseCounter, IDisposable
{
protected System.Diagnostics.PerformanceCounter pc;
private bool disposed;
public BaseCounter(string name)
{
disposed = false;
pc = CreatePerformanceCounter(name);
}
#region IBaseCounter
public string Name
{
get
{
if (disposed) throw new ObjectDisposedException("object has been disposed");
return pc.CounterName;
}
}
public string InstanceName
{
get
{
if (disposed) throw new ObjectDisposedException("object has been disposed");
return pc.InstanceName;
}
}
#endregion IBaseCounter
#region IDisposable
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
if (pc != null)
{
pc.Dispose();
}
pc = null;
disposed = true;
}
}
}
public void Dispose()
{
Dispose(true);
}
#endregion IDisposable
}
class ReadableCounter : BaseCounter, IReadableCounter //my own interface
{
public ReadableCounter(string name)
: base(name)
{
}
#region IReadableCounter
public Int64 CounterValue()
{
return pc.RawValue;
}
#endregion IReadableCounter
}
class WritableCounter : BaseCounter, IWritableCounter
{
public WritableCounter(string name)
: base(name)
{
}
#region IWritableCounter
public Increment()
{
pc.Increment();
}
#endregion IWritableCounter
}
No nosso sistema, ReadableCounter e WritableCounter são os únicos subclasses de BaseCounter e eles são apenas uma subclasse de mais um nível através de um processo de geração de código. O nível de subclasses adicional apenas addes um nome específico de modo que torna-se possível para criar objectos que correspondem directamente a contadores nomeados (por exemplo, se há um contador que é usado para contar o número de widgets produzidos, que acaba por ser encapsulada em uma classe WidgetCounter . WidgetCounter contém o conhecimento (na verdade, apenas o nome do contador como uma string) para permitir que o "WidgetCounter" contador de desempenho a ser criado.
Somente as classes gerado pelo código são usados ??diretamente pelos desenvolvedores, por isso, teria algo como isto:
class WritableWidgetCounter : WritableCounter
{
public WritableWidgetCounter
: base ("WidgetCounter")
{
}
}
class ReadableWidgetCounter : ReadableCounter
{
public ReadableWidgetCounter
: base ("WidgetCounter")
{
}
}
Então, você vê que a classe base detém e gere o objeto PerformanceCounter (que é descartável), enquanto as subclasses usar o PerformanceCounter.
Se eu tiver um código como este:
IWritableCounter wc = new WritableWidgetCounter();
wc.Increment();
wc.Dispose();
wc.Increment();
wc = null;
Como poderia WritableCounter sabe, em Incremento, que tinha sido eliminados? Should ReadableCoutner e WritableCounter simplesmente substituir o de BaseCounter
protected virtual void Dispose(bool disposing)
algo como isto:
protected virtual void Dispose(bool disposing)
{
disposed = true; //Nothing to dispose, simply remember being disposed
base.Dispose(disposing); //delegate to base
}
simplesmente para definir um WritableCounter de nível ReadableCounter / "eliminados" variável de membro?
E se a classe base (BaseCounter) declarou eliminados como protegido (ou fez uma propriedade protegida)? Dessa forma, as subclasses pode referir-se a ele em vez de adicionar um método Dispose simplesmente com a finalidade de lembrar que Descarte tinha acontecido.
Am I perder o barco sobre isso?
Solução
Eu vi algumas classes descartáveis ??com uma propriedade pública IsDisposed. Você poderia fazer isso e verificá-lo em suas sub-classes.
Outra coisa que eu tenho feito é um protegido método genérico 'Validar' que toda a sub-classe métodos de chamada (e poderia substituir). Se ela retorna, está tudo bem, caso contrário ele pode lançar. Isso seria isolar suas sub-classes das entranhas descartáveis ??por completo.
Outras dicas
Eu tenho trechos que eu uso para implementar IDisposable, tanto na classe base e nas subclasses. Você provavelmente quer um para a subclasse.
Eu furtei a maior parte deste código da MSDN, eu acho.
Aqui está o código para a classe base IDisposable (não o que você quiser):
#region IDisposable Members
// Track whether Dispose has been called.
private bool _disposed = false;
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
public void Dispose()
{
Dispose(true);
// Take yourself off the Finalization queue
// to prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user's code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
if (!this._disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if (disposing)
{
// TODO: Dispose managed resources.
}
// Release unmanaged resources. If disposing is false,
// only the following code is executed.
// TODO: Release unmanaged resources
// Note that this is not thread safe.
// Another thread could start disposing the object
// after the managed resources are disposed,
// but before the disposed flag is set to true.
// If thread safety is necessary, it must be
// implemented by the client.
}
_disposed = true;
}
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
~Program()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
#endregion
E aqui está o uso de código I nas subclasses (este é o código que você deseja):
#region IDisposable Members
// Track whether Dispose has been called.
private bool _disposed = false;
// Design pattern for a derived class.
// Note that this derived class inherently implements the
// IDisposable interface because it is implemented in the base class.
// This derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits
// them from the base class.
protected override void Dispose(bool disposing)
{
if (!this.disposed)
{
try
{
if (disposing)
{
// Release the managed resources you added in
// this derived class here.
// TODO: Dispose managed resources.
}
// Release the native unmanaged resources you added
// in this derived class here.
// TODO: Release unmanaged resources.
_disposed = true;
}
finally
{
// Call Dispose on your base class.
base.Dispose(disposing);
}
}
}
#endregion
Procure as marcas TODO:
.