classe base IDisposable que detém recurso gerenciado descartável, o que fazer em subclasses?

StackOverflow https://stackoverflow.com/questions/1015529

  •  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?

Foi útil?

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:.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top