Search code examples
c#.netusingdisposable

When is an object disposed if it uses 'using' and it is being used in a few methods


Look at this particular line of code: using var ws = new ClientWebSocket() in the class below. When is that object disposed taking into account that it uses using and it is being used in a few methods more like SendAsync, ReceiveAsync, etc.? If it was in a regular method like Main(), it usually gets disposed at the closing branch, but in this case it's not disposed there. This class also doesn't inherit IDisposable. I removed the unnecessary details from the class.

public class Client
{
    private ClientWebSocket? _ws;

    private async Task<bool> ConnectAsync(CancellationToken cancellationToken)
    {
        using var ws = new ClientWebSocket();
        _ws = ws;

        ...
    }

    public async Task SendAsync(string data)
    {
        ...

        await _ws.SendAsync(dataBytes, WebSocketMessageType.Text, true, CancellationToken.None);
    }
}

Solution

  • Your using statement is C#'s new declaration style of using introduced in C# version 8, meaning you no longer have unnecessary nesting for your statements. However the principals are still the same. Consider the following way of writing your code prior to C# 8:

    public class Client
    {
        private ClientWebSocket? _ws;
    
        private async Task<bool> ConnectAsync(CancellationToken cancellationToken, string data)
        {
            using (var ws = new ClientWebSocket())
            {
                // When your code leaves this block, your websocket is disposed
                ws.SendAsync(dataBytes, WebSocketMessageType.Text, true, CancellationToken.None);
                
                // Assigning ws to _ws isn't necessary as it isn't usable outside your using statement
                _ws = ws;
                
                // The end of your using block - the websocket will now be disposed
            }
            
            // This line would fail as your websocket has been disposed
            _ws.SendAsync(dataBytes, WebSocketMessageType.Text, true, CancellationToken.None);
        }
    }
    

    The dispose method gets called when your code leaves the using block. A declaration means that the scope after which it will be disposed of is now the block of code that contains the using declaration, in this instance, your ConnectAsync method. So even though you still have a reference to it when it's assigned to _ws, it can't be used as it's already been disposed when your code left this method block.