Existem efeitos colaterais de retornar de dentro de uma instrução using ()?

125

Retornar um valor de método de dentro de uma instrução using que obtém um DataContext parece sempre funcionar bem , assim:

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;
    }
}

Mas eu sempre sinto que eu deveria ser fechar algo antes de eu sair da usando colchetes, por exemplo, através da definição de transação antes a instrução usando, obtê-lo de valor dentro dos colchetes, e depois retornando após os suportes.

Definir e retornar a variável fora dos colchetes usando seria uma prática melhor ou conservaria os recursos de alguma maneira?

Edward Tanguay
fonte
1
Pode ser interessante olhar para a IL geral para variantes disso. Eu suspeito que haveria pouca diferença na IL gerada. Normalmente, eu nem me incomodaria em declarar a transação var - basta retornar o resultado da expressão.
Jonesie

Respostas:

164

Não, acho que é mais claro assim. Não se preocupe, Disposeainda será chamado de "saída" - e somente depois que o valor de retorno for totalmente avaliado. Se uma exceção for lançada a qualquer momento (incluindo a avaliação do valor de retorno), Disposetambém será chamada também.

Embora você certamente possa seguir o caminho mais longo, são duas linhas extras que apenas adicionam cruft e contexto extra para acompanhar (mentalmente). De fato, você realmente não precisa da variável local extra - embora possa ser útil em termos de depuração. Você poderia apenas ter:

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();
    }
}

Na verdade, posso até ficar tentado a usar a notação de ponto e colocar a Wherecondição 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
fonte
2
Sine é você @jon, ainda é seguro se uma exceção for lançada dentro do bloco usando?
31410 Dave Archer #
6
sim. usando é simplesmente açúcar sintático para uma tentativa / finalmente construo
Mitch trigo
@ David: Como Mitch diz, está tudo bem - eu atualizei a resposta para fazer com que mais clara :)
Jon Skeet
2
Por que usar OrderByDescending em combinação com SingleOrDefault?
precisa saber é o seguinte
2
@erikkallen: O LINQ não possui um "MaxBy", infelizmente - portanto, você não pode obter a linha com o valor máximo. Para o LINQ to Objects, você pode escrever o seu com bastante facilidade, mas não tenho certeza de uma maneira melhor de fazê-lo neste caso. O que você sugeriria?
precisa
32

Veja isso

Compreendendo a instrução 'using' em C #

O CLR converte seu código em MSIL. E a instrução using é traduzida em uma tentativa e, finalmente, em um bloco. É assim que a instrução using é representada em IL. Uma declaração de uso é traduzida em três partes: aquisição, uso e descarte. O recurso é adquirido primeiro e, em seguida, o uso é incluído em uma instrução try com uma cláusula finally. O objeto é descartado na cláusula Finalmente.

Adriaan Stander
fonte
4
Uma visão interessante. Obrigado.
Kangkan
1
Isso traduz a pergunta para: Algum efeito colateral do retorno do bloco de tentativa de uma tentativa finalmente?
Henk Holterman
3
Não, o finalmente sempre será chamado. techinterviews.com/interview-questions-for-c-developers
Adriaan Stander
6

Não efeitos colaterais de retornar de dentro de uma using()declaração.

Se torna o código mais legível é outra discussão.

Mitch Wheat
fonte
0

Eu acho que é tudo a mesma coisa. Não há nada de ruim no código. A estrutura .NET não se importaria onde o objeto é criado. O que importa é se é referenciado ou não.

Kerido
fonte
-1

Sim, pode haver um efeito colateral. Por exemplo, se você usar a mesma técnica no método de ação do ASP.NET MVC, receberá o seguinte erro: "A instância do ObjectContext foi descartada e não pode mais ser usada para operações que exigem uma conexão"

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);
    }
}
usefulBee
fonte
2
se você definir uma transação fora da instrução using, receberá o mesmo erro. using keyword não está relacionado neste caso.
Costa