¿Es apropiado 'usar' en un contexto donde no hay nada que desechar?

9

En C #, la usingdeclaración se utiliza para disponer de manera determinista los recursos sin esperar al recolector de basura. Por ejemplo, se puede usar para:

  • Deseche comandos SQL o conexiones,

  • Cerrar transmisiones, liberando la fuente subyacente como un archivo,

  • Elementos GDI + gratuitos,

  • etc.

Noté que usingse usa cada vez más en casos donde no hay nada que desechar, pero donde es más conveniente para la persona que llama escribir un usingbloque en lugar de dos comandos separados.

Ejemplos:

  • MiniProfiler , escrito por el equipo de Stack Overflow, usa usingpara denotar bloques para perfilar:

    using (profiler.Step("Name goes here"))
    {
        this.DoSomethingUseful(i - 1);
    }

    Un enfoque alternativo sería tener dos bloques:

    var p = profiler.Start("Name goes here");
    this.DoSomethingUseful(i - 1);
    profiler.Stop(p);

    Otro enfoque sería utilizar acciones:

    profiler.Step("Name goes here", () => this.DoSomethingUseful(i - 1));
  • ASP.NET MVC también elegido usingpara formularios:

    <% using (Html.BeginForm())
       { %>
           <label for="firstName">Name:</label>
           <%= Html.TextBox("name")%>
           <input type="submit" value="Save" />    
    <% } %>

¿Es apropiado ese uso? Cómo justificarlo, dado que hay varios inconvenientes:

  • Los principiantes se perderían, ya que dicho uso no se corresponde con el que se explica en los libros y las especificaciones del idioma,

  • El código debe ser expresivo. Aquí, la expresividad sufre, ya que el uso apropiado de usinges mostrar que detrás hay un recurso, como una secuencia, una conexión de red o una base de datos que debe liberarse sin esperar al recolector de basura.

Arseni Mourzenko
fuente
2
El enfoque de tener dos declaraciones individuales sufre el mismo problema que la gestión ingenua de recursos sin using: profiler.Stop(p)No se garantiza que la última declaración (por ejemplo ) se ejecute ante las excepciones y el flujo de control.
1
@delnan: esto explica por qué MiniProfiler evitó el segundo enfoque. Pero el segundo debe evitarse en todos los casos, ya que es difícil de escribir y mantener.
Arseni Mourzenko
1
Relacionado: stackoverflow.com/questions/9472304
Robert Harvey
1
Relacionado: grabbagoft.blogspot.com/2007/06/…
Robert Harvey

Respuestas:

7

Su última afirmación: que "el uso apropiado del uso es mostrar que detrás hay un recurso, como un flujo, una conexión de red o una base de datos que debería liberarse sin esperar al recolector de basura" es incorrecto, y la razón es en la documentación de la interfaz IDisposable: http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

El uso principal de esta interfaz es liberar recursos no administrados.

Por lo tanto, si su clase utiliza recursos no administrados, no importa cuándo quiera o no que ocurra GC: no tiene nada que ver con GC ya que los recursos no administrados no son GC'ed ( https: // stackoverflow. com / questiones / 3607213 / what-is-mean-by-managed-vs-unmanaged-resources-in-net ).

Entonces, el propósito de "usar" no es evitar esperar en GC, es forzar una liberación de esos recursos no administrados ahora , antes de que la instancia de clase salga del alcance y se llame Finalizar. Esto es importante por razones que deberían ser obvias: un recurso no administrado puede depender de otros recursos no administrados, y si se eliminan (o finalizan) en el orden incorrecto, pueden suceder cosas malas.

Entonces, si la clase que se instancia en el bloque de uso usa recursos no administrados, entonces la respuesta es sí, es apropiado.

Sin embargo, tenga en cuenta que IDisposable no es prescriptivo sobre si es solo para liberar recursos no administrados, solo que este es su propósito principal . Se puede darse el caso de que el autor de la clase tiene algún tipo de acción que ellos quieren hacer cumplir sucediendo en un momento determinado, y la aplicación de IDisposable puede ser una manera de conseguir que esto suceda, pero si eso es una solución elegante es algo que puede solo se responderá caso por caso.

Cualquiera que sea, el uso de "usar" implica que la clase implementa IDisposable, por lo que no viola la expresividad del código; De hecho, deja bastante claro lo que está sucediendo.

Maximus Minimus
fuente
"El uso principal de esta interfaz es liberar recursos no administrados". Yo digo que la documentación de Microsoft apesta en ese punto. Tal vez así es como lo usan en el FCL, pero los desarrolladores de aplicaciones lo han estado utilizando para una variedad de cosas durante casi una década, algunas por buenas razones, otras por razones no tan buenas. Pero citar esa parte de su documento no responde a la pregunta del OP en absoluto.
Jesse C. Slicer
1
Tenga en cuenta mi segundo último párrafo: el propósito principal no tiene por qué ser el único . IDisposable se puede implementar por cualquier motivo, y proporciona una interfaz conocida con el comportamiento esperado y el estilo de uso, por lo que cuando lo vea, sabrá que hay algo más que debe suceder antes de que la instancia se salga del alcance. El punto es: si implementa IDisposable, entonces usar es apropiado; todo lo demás es solo preámbulo.
Maximus Minimus
1
Básicamente estás diciendo que el propósito no es evitar esperar a GC, es evitar esperar al finalizador. Pero no veo la diferencia: el GC llama al finalizador.
svick
El GC funciona de manera no determinista: no sabe cuándo se llamará. El GC también solo llama al finalizador, pero no se deshace solo: el recurso en sí está fuera del control del GC. Con el uso de (1), el objeto tiene el alcance adecuado, y (2) se sabe que la eliminación ocurre en un punto conocido en el tiempo: cuando sale del alcance del bloque de uso, el recurso no administrado desaparece (o, en el peor de los casos) a algo más que lo destruirá). Realmente, esto es solo una trampa; lea la documentación, es todo lo que hay, no debería necesitar repetirse.
Maximus Minimus
8

El uso de usingimplica la presencia de un Dispose()método. Otros programadores supondrán que dicho método existe en el objeto. En consecuencia, si un objeto no es desechable, no debe usarlo using.

La claridad del código es el rey. Omita usingo implemente IDisposableen el objeto.

MiniProfiler parece estar utilizando usingcomo mecanismo para "cercar" el código que se perfila. Hay algo de mérito en esto; presumiblemente, MiniProfiler está llamando Dispose()para detener un temporizador o el temporizador se detiene cuando el objeto MiniProfiler sale del alcance.

En términos más generales, invocaría usingcuando algún tipo de finalización deba ocurrir automáticamente. La documentación para los html.BeginFormestados que, cuando el método se utiliza en una usingdeclaración, representa la </form>etiqueta de cierre al final del usingbloque.

Eso no significa necesariamente que todavía no sea abuso.

Robert Harvey
fuente
44
Hasta ahora tan obvio. La pregunta es, ¿cuándo (si es que es apropiado) implementar IDisposable/ a Dispose()pesar de no tener nada que desechar en el sentido que OP describe?
2
Pensé que lo había dejado claro; allí no es un momento en el que sea apropiado.
Robert Harvey
1
Mi punto es, Disposees necesario para usingque tenga sentido (independientemente de si es apropiado). Todos los ejemplos de OP implementan Dispose, ¿no? Y IIUC, la pregunta es si es correcto que estas clases usen Dispose, en lugar de algún otro método (como Stop()MiniProfiler).
1
Sí, pero esa no es la pregunta. La pregunta es si es una buena idea hacerlo.
2
@delnan: Nuevamente, pensé que lo había dejado claro. Si aclara la intención de su código, entonces es una buena idea. Si no es así, no lo es.
Robert Harvey
1

usingEs seguro el error y la excepción. Asegura Dispose()que se llamará independientemente de cualquier error que cometa el programador. No interfiere con la captura o el manejo de excepciones, pero el Dispose()método se ejecuta recursivamente en la pila cuando se produce una excepción.

Objetos que se implementan IDisposepero que no tienen nada que desechar cuando se hacen. En el mejor de los casos, a prueba de futuro de su diseño. Para que no tenga que refactorizar su código fuente, cuando en el futuro, ellos tengan que deshacerse de algo.

Reactgular
fuente