using keyword with private member assignment to manage IDisposable

Viewed 58

I was reading Stephen Cleary's blog post about cancellation, and I saw something I haven't seen before in the following code snippet.

Constructor() => CancelButton.Enabled = false;

private CancellationTokenSource? _cts;

async void StartButton_Click(..)
{
    StartButton.Enabled = false;
    CancelButton.Enabled = true;

    // Create a CTS for manual cancellation requests.
    using var cts = _cts = new();

    try
    {
        // Pass the token for that CTS to lower-level code.
        await DoSomethingAsync(_cts.Token);
        .. // Display success in UI.
    }
    catch (Exception ex)
    {
        .. // Display error in UI.
    }
    finally
    {
        StartButton.Enabled = true;
        CancelButton.Enabled = false;
    }
}

async void CancelButton_Click(..)
{
    // Manually cancel the CTS.
    _cts!.Cancel();
}

Particularly, the using var cts = _cts = new(); line.

Is the using managing the disposal of the private member as well as the local variable?

Why assign the local variable if it's not used anywhere?

What is going on here?

1 Answers

It's simply because you can't do this:

using _cts = new();

That will give a compile error, "Identifier expected".

So he's introduced a local variable which will be disposed at the end of the method:

using var cts = _cts = new();

Now it will compile, and cts will be disposed (NOTE: it does NOT dispose _cts; it will only dispose the introduced local variable, cts).

He wanted to initialise a field, _cts, and have the created object automatically disposed at the end of the method, and this is a simple way to do so.

This would be the alternative way of writing it:

_cts = new();
using var cts = _cts;

An alternative way of writing this that might look less weird is to have a method that initialises _cts and returns it:

CancellationTokenSource initialiseCancellationToken()
{
    var cts = new CancellationTokenSource();
    _cts = cts;
    return cts;
}

Then you'd write the less suprising:

using var cts = initialiseCancellationToken();

You pays yer money and you makes yer choice!

Note: You might be tempted to write this:

CancellationTokenSource initialiseCancellationToken()
{
    _cts = new CancellationTokenSource();
    return _cts;
}

But that has a subtle race condition! Can you spot it (assuming that initialiseCancellationToken() can be called by multiple threads simultaneously)?

Related