metodo - using c#




Finalizar/Eliminar patrón en C# (9)

  1. Si está utilizando otros objetos administrados que utilizan recursos no administrados, no es su responsabilidad asegurarse de que estén finalizados. Su responsabilidad es llamar a Dispose en esos objetos cuando Dispose se llame a su objeto y se detenga allí.

  2. Si su clase no utiliza ningún recurso escaso, no veo por qué haría que su implemento de clase fuera IDisponible. Solo debes hacerlo si eres:

    • Sepa que pronto tendrá escasos recursos en sus objetos, pero no ahora (y quiero decir que como en "todavía estamos desarrollando, estará aquí antes de que terminemos", no como en "creo que necesitaremos esto ")
    • Usando recursos escasos
  3. Sí, el código que usa su código debe llamar al método Dispose de su objeto. Y sí, el código que usa tu objeto puede usar using como lo has mostrado.

  4. (¿2 de nuevo?) Es probable que WebClient use recursos no administrados u otros recursos administrados que implementen IDisposable. La razón exacta, sin embargo, no es importante. Lo importante es que implementa IDisposable y, por lo tanto, le corresponde a usted actuar sobre ese conocimiento al desechar el objeto cuando haya terminado con él, incluso si resulta que WebClient no utiliza ningún otro recurso.

C # 2008

He estado trabajando en esto por un tiempo y todavía estoy confundido acerca de algunos problemas. Mis preguntas están abajo

  1. Sé que solo necesita un finalizador si está desechando recursos no administrados. Sin embargo, si está utilizando recursos administrados que realizan llamadas a recursos no administrados, ¿todavía necesitaría implementar un finalizador?

  2. Sin embargo, si desarrolla una clase que no usa ningún recurso no administrado, directa o indirectamente, ¿puede implementar el IDisposable para que los clientes de su clase puedan usar la 'declaración de uso'?

    ¿Sería aceptable implementar el IDisposable solo para que los clientes de su clase puedan usar la declaración de uso?

    using(myClass objClass = new myClass())
    {
        // Do stuff here
    }
    
  3. He desarrollado este sencillo código a continuación para demostrar el patrón de Finalizar / Eliminar:

    public class NoGateway : IDisposable
    {
        private WebClient wc = null;
    
        public NoGateway()
        {
            wc = new WebClient();
            wc.DownloadStringCompleted += wc_DownloadStringCompleted;
        }
    
    
        // Start the Async call to find if NoGateway is true or false
        public void NoGatewayStatus()
        {
            // Start the Async's download
                // Do other work here
            wc.DownloadStringAsync(new Uri(www.xxxx.xxx));
        }
    
        private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            // Do work here
        }
    
        // Dispose of the NoGateway object
        public void Dispose()
        {
            wc.DownloadStringCompleted -= wc_DownloadStringCompleted;
            wc.Dispose();
            GC.SuppressFinalize(this);
        }
    }
    

Pregunta sobre el código fuente:

  1. Aquí no he agregado el finalizador, y normalmente el finalizador será llamado por el GC, y el finalizador llamará al Dispose. Como no tengo el finalizador, ¿cuándo llamo al método de Disposición? ¿Es el cliente de la clase que tiene que llamarlo?

    Así que mi clase en el ejemplo se llama NoGateway y el cliente podría usar y disponer de la clase de esta manera:

    using(NoGateway objNoGateway = new NoGateway())
    {
        // Do stuff here   
    }
    

    ¿Se llamaría automáticamente al método de Disposición cuando la ejecución llegue al final del bloque de uso, o el cliente tiene que llamar manualmente al método de disposición? es decir

    NoGateway objNoGateway = new NoGateway();
    // Do stuff with object
    objNoGateway.Dispose(); // finished with it
    
  2. Estoy usando la clase NoGateway en mi clase NoGateway . Debido a que el cliente web implementa la interfaz IDisposable, ¿significa esto que el cliente web utiliza indirectamente recursos no administrados? ¿Hay una regla difícil y rápida a seguir sobre esto? ¿Cómo sé que una clase usa recursos no administrados?


1) WebClient es un tipo administrado, por lo que no necesita un finalizador. El finalizador es necesario en el caso de que sus usuarios no desechen () de su clase NoGateway y el tipo nativo (que no sea recopilado por el GC) deba limpiarse después. En este caso, si el usuario no llama a Dispose (), el cliente eliminará el WebClient contenido justo después de que lo haga NoGateway.

2) Indirectamente, sí, pero no debes preocuparte por eso. Su código es correcto tal como está y no puede evitar que sus usuarios se olviden de Dispose () muy fácilmente.


El patrón IDisponible recomendado está here . Al programar una clase que usa IDisposable, generalmente debe usar dos patrones:

Cuando implementa una clase sellada que no usa recursos no administrados, simplemente implementa un método Dispose como en las implementaciones de interfaz normales:

public sealed class A : IDisposable
{
    public void Dispose()
    {
        // get rid of managed resources, call Dispose on member variables...
    }
}

Cuando implementes una clase sin sellar, hazlo así:

public class B : IDisposable
{    
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }   
        // get rid of unmanaged resources
    }

    // only if you use unmanaged resources directly in B
    //~B()
    //{
    //    Dispose(false);
    //}
}

Tenga en cuenta que no he declarado un finalizador en B ; solo debe implementar un finalizador si tiene recursos no administrados reales para disponer. El CLR trata los objetos finalizables de forma diferente a los objetos no finalizables, incluso si se llama a SuppressFinalize .

Por lo tanto, no debe declarar un finalizador a menos que tenga que hacerlo, pero le da a los herederos de su clase un gancho para llamar a su Dispose e implementar un finalizador si usan recursos no administrados directamente:

public class C : B
{
    private IntPtr m_Handle;

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            // get rid of managed resources
        }
        ReleaseHandle(m_Handle);

        base.Dispose(disposing);
    }

    ~C() {
        Dispose(false);
    }
}

Si no está utilizando los recursos no administrados directamente ( SafeHandle y sus amigos no cuentan, ya que declaran sus propios finalizadores), entonces no implemente un finalizador, ya que el GC trata las clases finalizables de manera diferente, incluso si luego suprime el finalizador. . También tenga en cuenta que, aunque B no tiene un finalizador, todavía llama a SuppressFinalize para tratar correctamente las subclases que implementan un finalizador.

Cuando una clase implementa la interfaz IDisposable, significa que en algún lugar hay algunos recursos no administrados que deben eliminarse cuando haya terminado de usar la clase. Los recursos reales están encapsulados dentro de las clases; No es necesario que los elimines explícitamente. Simplemente llamando a Dispose() o envolviendo la clase en un using(...) {} se asegurará de que los recursos no administrados se eliminen según sea necesario.


El patrón oficial para implementar IDisposable es difícil de entender. Creo que este es better :

public class BetterDisposableClass : IDisposable {

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

  protected virtual void CleanUpManagedResources() { 
    // ...
  }
  protected virtual void CleanUpNativeResources() {
    // ...
  }

  ~BetterDisposableClass() {
    CleanUpNativeResources();
  }

}

Una solución aún mejor es tener una regla de que siempre debe crear una clase contenedora para cualquier recurso no administrado que necesite manejar:

public class NativeDisposable : IDisposable {

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

  protected virtual void CleanUpNativeResource() {
    // ...
  }

  ~NativeDisposable() {
    CleanUpNativeResource();
  }

}

Con SafeHandle y sus derivados, estas clases deberían ser muy raras .

El resultado para las clases desechables que no tratan directamente con recursos no administrados, incluso en presencia de herencia, es poderoso: ya no necesitan preocuparse por los recursos no administrados . Serán simples de implementar y entender:

public class ManagedDisposable : IDisposable {

  public virtual void Dispose() {
    // dispose of managed resources
  }

}

Estoy de acuerdo con pm100 (y debería haberlo dicho explícitamente en mi publicación anterior).

Nunca debe implementar IDisposable en una clase a menos que lo necesite. Para ser muy específicos, hay aproximadamente 5 veces cuando alguna vez necesitarías / deberías implementar IDisposable:

  1. Su clase contiene explícitamente (es decir, no a través de la herencia) los recursos administrados que implementan IDisposable y deben limpiarse una vez que su clase ya no se use. Por ejemplo, si su clase contiene una instancia de Stream, DbCommand, DataTable, etc.

  2. Su clase contiene explícitamente cualquier recurso administrado que implemente un método Close (), por ejemplo, IDataReader, IDbConnection, etc. Tenga en cuenta que algunas de estas clases implementan IDisposable al tener Dispose () así como un método Close ().

  3. Su clase contiene explícitamente un recurso no administrado, por ejemplo, un objeto COM, punteros (sí, puede usar punteros en C # administrado pero deben declararse en bloques 'no seguros', etc. En el caso de recursos no administrados, también debe asegurarse de llame a System.Runtime.InteropServices.Marshal.ReleaseComObject () en el RCW. Aunque el RCW es, en teoría, una envoltura administrada, todavía hay un conteo de referencias en las coberturas.

  4. Si su clase se suscribe a eventos usando referencias fuertes. Necesita darse de baja / desvincularse de los eventos. ¡Siempre asegúrese de que estos no sean nulos antes de intentar desregistrarlos / separarlos!

  5. Su clase contiene cualquier combinación de lo anterior ...

Una alternativa recomendada para trabajar con objetos COM y tener que usar Marshal.ReleaseComObject () es usar la clase System.Runtime.InteropServices.SafeHandle.

El BCL (Base Class Library Team) tiene una buena publicación de blog al respecto aquí blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx

Una nota muy importante que debe hacer es que si está trabajando con WCF y limpiando recursos, SIEMPRE debe evitar SIEMPRE el bloque 'usar'. Hay muchas publicaciones en el blog y algunas en MSDN sobre por qué es una mala idea. También he publicado sobre esto aquí - No use 'usar ()' con un proxy WCF


Por lo que sé, es altamente recomendable NO usar el Finalizador / Destructor:

public ~MyClass() {
  //dont use this
}

Sobre todo, esto se debe a no saber cuándo o SI se llamará. El método de desechar es mucho mejor, especialmente si usamos o desechamos directamente.

el uso es bueno utilízalo :)


Usando lambdas en lugar de IDisposable.

Nunca me ha entusiasmado la idea general utilizando / IDisposable. El problema es que requiere que la persona que llama:

  • Saber que deben usar IDisposable.
  • recuerde usar 'usar'.

Mi nuevo método preferido es usar un método de fábrica y un lambda en su lugar

Imagina que quiero hacer algo con un SqlConnection (algo que debería estar envuelto en un uso). Clásicamente harías

using (Var conn = Factory.MakeConnection())
{
     conn.Query(....);
}

Nueva manera

Factory.DoWithConnection((conn)=>
{
    conn.Query(...);
}

En el primer caso, la persona que llama simplemente no puede usar la sintaxis de uso. En el segundo caso el usuario no tiene otra opción. No hay ningún método que cree un objeto SqlConnection, el llamador debe invocar DoWithConnection.

DoWithConnection se ve así

void DoWithConnection(Action<SqlConnection> action)
{
   using (var conn = MakeConnection())
   {
       action(conn);
   }
}

MakeConnection ahora es privado


nadie respondió la pregunta sobre si debe implementar IDisposable aunque no lo necesite.

Respuesta corta: No

Respuesta larga:

Esto permitiría que un consumidor de su clase use 'usar'. La pregunta que me gustaría hacer es: ¿por qué lo harían? La mayoría de los desarrolladores no usarán "usar" a menos que sepan que deben hacerlo, y cómo lo saben. Ya sea

  • es obvio el de la experiencia (una clase de socket por ejemplo)
  • esta documentado
  • son cautelosos y pueden ver que la clase implementa IDisposable

Entonces, al implementar IDisposable, le está diciendo a los desarrolladores (al menos a algunos) que esta clase envuelve algo que debe ser publicado. Utilizarán "usar", pero hay otros casos en los que no es posible usar (el alcance del objeto no es local); y tendrán que comenzar a preocuparse por la vida útil de los objetos en esos otros casos, me preocuparía con seguridad. Pero esto no es necesario

Usted implementa Idisposable para permitirles usar, pero no lo usarán a menos que usted lo indique.

Así que no lo hagas


using(NoGateway objNoGateway = new NoGateway())

es equivalente a

try
{
    NoGateway = new NoGateway();
}

finally
{
    NoGateway.Dispose();
}

Se llama a un finalizador al CG que destruye tu objeto. Esto puede ser en un momento totalmente diferente que cuando abandonas tu método. El desecho de IDisposable se llama inmediatamente después de que abandone el bloque de uso. Por lo tanto, el patrón suele ser usar el uso de recursos libres inmediatamente después de que ya no los necesite.





finalizer