Home > Enterprise >  using keyword with private member assignment to manage IDisposable
using keyword with private member assignment to manage IDisposable

Time:06-29

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?

CodePudding user response:

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