Refactoring: using statement without scope, when does the implicit `Dispose` call happen?
Asked Answered
F

3

10

I was refactoring some the other day, I bumped into something like that:

public async Task<Result> Handle(CancelInitiatedCashoutCommand command, CancellationToken cancellationToken)
{
    using (_logger.BeginScope("{@CancelCashoutCommand}", command))
    {
        return await GetCashoutAsync(command.CashoutId)
            .Bind(IsStatePending)
            .Tap(SetCancelledStateAsync)
            .Tap(_ => _logger.LogInformation("Cashout cancellation succeeded."));
    }
}

and ReSharper suggested to refactor it as:

public async Task<Result> Handle(CancelInitiatedCashoutCommand command, CancellationToken cancellationToken)
{
    using var scope = _logger.BeginScope("{@CancelCashoutCommand}", command);
    return await GetCashoutAsync(command.CashoutId)
        .Bind(IsStatePending)
        .Tap(SetCancelledStateAsync)
        .Tap(_ => _logger.LogInformation("Cashout cancellation succeeded."));
}

I am a bit skeptical, actually I am not sure when the implicit Dispose call will happen with the second version.

How can I know?

Ferity answered 11/12, 2019 at 11:41 Comment(5)
using var scope = ... ; means that scope will be Disposed on leaving Handle scopeFoetor
So basically the method containing that statement.Ferity
Did you have a look at the documentation? If so, what's not clear about it? By the way you could always test this by implementing your own test class public class TestDispose : IDisposable { public void Dispose(){ Console.WriteLine("disposing"); }} to see when the output is called.Doublefaced
For things like logging scopes and transactions, the code arguably remains clearer if the scope remains explicitly indicated with a block, doubly so because logging scopes aren't normally named. The using var is useful if, functionally, you don't really care when exactly the resource is disposed as long as it happens (e.g. SqlConnection). In all cases the Dispose moment is deterministic, but with a using var the scope can be left unsaid when it's not particularly important. Of course this is subjective and that's why Resharper sticks to suggestions.Meghan
I did check the documentation but it looked really odd and feels really off. I also checked by myself and yea the dispose was called after but I was not sure if the compiler could have changed the rewriting (and the implicit Dispose call) under some other circumstancesFerity
F
6

Resharper suggests C# 8.0 using declaration feature:

 public async Task<Result> Handle(CancelInitiatedCashoutCommand command, 
                                  CancellationToken cancellationToken)
 {  
    using var scope = ...;
    ...
 } // <- scope will be Disposed on leaving its scope (here on Handle method's scope)
Foetor answered 11/12, 2019 at 11:45 Comment(0)
M
5

That is a C#8 using statement, and the object referenced by scope is disposed when the variable itself goes out of scope.

In this case, that would be after your Task has completed.

Microlith answered 11/12, 2019 at 11:45 Comment(0)
G
4

I was wondering the same thing. The using declaration moves out of scope at the end of the method, and is only disposed of then. Microsoft docs states the following:

the file is disposed when the closing brace for the method is reached. That's the end of the scope in which file is declared.

It would seem that if you have a using statement, it would dispose of the variable at the end of the using brace, as opposed to the using declaration that only disposes of the variable at the end of the method. If you view this in the watch or locals window, you will immediately see it move out of scope. https://dirkstrauss.com/c-sharp-8-0-using-declarations/

Giblets answered 3/2, 2020 at 19:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.