¿Hay algún efecto secundario de regresar desde adentro de una instrucción using ()?

125

Devolver un valor de método desde el interior de una instrucción de uso que obtiene un DataContext parece funcionar siempre bien , así:

public static Transaction GetMostRecentTransaction(int singleId)
{
    using (var db = new DataClasses1DataContext())
    {
        var transaction = (from t in db.Transactions
                              orderby t.WhenCreated descending
                              where t.Id == singleId
                              select t).SingleOrDefault();
        return transaction;
    }
}

Pero siempre siento que debería cerrar algo antes de salir de los corchetes de uso, por ejemplo, al definir la transacción antes de la declaración de uso, obtener su valor dentro de los corchetes y luego regresar después de los corchetes.

¿Definir y devolver la variable fuera de los corchetes de uso sería una mejor práctica o conservaría los recursos de alguna manera?

Edward Tanguay
fuente
1
Puede ser interesante mirar la IL general para variantes de esto. Sospecho que habría poca diferencia en la IL generada. Normalmente ni siquiera me molestaría en declarar la transacción var, solo devuelvo el resultado de la expresión.
Jonesie

Respuestas:

164

No, creo que es más claro de esta manera. No se preocupe, Disposeseguirá llamándose "al salir", y solo después de que el valor de retorno se haya evaluado por completo. Si se produce una excepción en algún momento (incluida la evaluación del valor de retorno), Disposese seguirá llamando también.

Si bien ciertamente podría tomar la ruta más larga, son dos líneas adicionales que solo agregan cruft y contexto adicional para realizar un seguimiento (mentalmente). De hecho, realmente no necesita la variable local adicional, aunque puede ser útil en términos de depuración. Usted podría simplemente tener:

public static Transaction GetMostRecentTransaction(int singleId)
{
    using (var db = new DataClasses1DataContext())
    {
        return (from t in db.Transactions
                orderby t.WhenCreated descending
                where t.Id == singleId
                select t).SingleOrDefault();
    }
}

De hecho, incluso podría sentir la tentación de usar la notación de puntos y poner la Wherecondición dentro de SingleOrDefault:

public static Transaction GetMostRecentTransaction(int singleId)
{
    using (var db = new DataClasses1DataContext())
    {
        return db.Transactions.OrderByDescending(t => t.WhenCreated)
                              .SingleOrDefault(t => t.Id == singleId);
    }
}
Jon Skeet
fuente
2
Seno, eres tú @ Jon, ¿sigue siendo seguro si se lanza una excepción dentro del bloque de uso?
Dave Archer
66
si. usar es simplemente azúcar sintáctico para un intento / finalmente construir
Mitch Wheat
@David: Como dice Mitch, está bien - He actualizado la respuesta para aclararlo :)
Jon Skeet
2
¿Por qué usar OrderByDescending en combinación con SingleOrDefault?
erikkallen
2
@erikkallen: LINQ no tiene un "MaxBy", desafortunadamente, por lo que no puede obtener la fila con el valor máximo. Para LINQ to Objects, puede escribir el suyo con bastante facilidad, pero no estoy seguro de una mejor manera de hacerlo en este caso. ¿Qué sugerirías en su lugar?
Jon Skeet
32

Echa un vistazo a esto

Comprender la instrucción 'using' en C #

El CLR convierte su código en MSIL. Y la declaración de uso se traduce en un intento y finalmente se bloquea. Así es como se representa la instrucción using en IL. Una declaración de uso se traduce en tres partes: adquisición, uso y eliminación. El recurso se adquiere primero, luego el uso se incluye en una declaración de prueba con una cláusula finalmente El objeto se elimina en la cláusula final.

Adriaan Stander
fuente
44
Una idea interesante. Gracias.
Kangkan
1
Eso traduce la pregunta a: ¿Algún efecto secundario de regresar del try-block de un try-finally?
Henk Holterman
3
No, al final siempre se llamará. techinterviews.com/interview-questions-for-c-developers
Adriaan Stander
6

No hay efectos secundarios de regresar desde dentro de una using()declaración.

Si hace el código más legible es otra discusión.

Trigo Mitch
fuente
0

Creo que es todo lo mismo. No hay nada malo en el código. El marco .NET no le importaría dónde se crea el objeto. Lo que importa es si se hace referencia o no.

Kerido
fuente
-1

Sí, puede haber un efecto secundario. Por ejemplo, si usa la misma técnica en el método de Acción ASP.NET MVC, obtendrá el siguiente error: "La instancia de ObjectContext se ha eliminado y ya no se puede usar para operaciones que requieren una conexión"

public ActionResult GetMostRecentTransaction(int singleId)
{
    using (var db = new DataClasses1DataContext())
    {
        var transaction = (from t in db.Transactions
                              orderby t.WhenCreated descending
                              where t.Id == singleId
                              select t).SingleOrDefault();
        return PartialView("_transactionPartial", transaction);
    }
}
útilBee
fuente
2
si define la transacción fuera del uso de la declaración, obtendrá el mismo error. El uso de la palabra clave no está relacionado en este caso.
Costa