使用关键字处理私有成员分配以管理IDisposable。

3

我在阅读Stephen Cleary关于取消的博客文章时,在下面的代码片段中看到了我以前从未见过的东西。

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

特别是,using var cts = _cts = new(); 这行代码。

using 关键字是否同时管理私有成员和局部变量的处理?

如果局部变量未被使用,为什么要进行赋值?

这段代码在做什么?


它在本地变量的值上调用Dispose() - 但它也被分配给了字段。 - Jon Skeet
那么,在这种情况下,Dispose() 是什么时候被调用的?当 cts_cts 都超出范围时吗? - brent.reynolds
@brent.reynolds 请看我的回答。_cts从未被直接处理(如果您查看代码片段,这样做是不安全的,因为它可能会被另一个线程覆盖)。 - Matthew Watson
1个回答

3

这是因为您无法做到以下操作:

using _cts = new();

会导致编译错误,提示“需要标识符”。

因此他引入了一个局部变量,在方法结束时将被销毁:

using var cts = _cts = new();

现在代码可以编译了,而cts将被处理(注意:它不会处理_cts;它只会处理引入的局部变量cts)。
他想要初始化一个字段_cts,并希望在方法结束时自动处理创建的对象,这是实现这一点的简单方式。
以下是替代的写法:
_cts = new();
using var cts = _cts;

另一种可能看起来不那么奇怪的编写方式是有一个方法初始化_cts并返回它:

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

然后您会写下不太令人惊讶的部分:

using var cts = initialiseCancellationToken();

你出钱,你做决定!

注意:你可能会被诱惑写成这样:

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

但是这里存在一个微妙的竞态条件!你能发现它吗(假设initialiseCancellationToken() 可以被多个线程同时调用)?


虽然您说的是正确的,即Dispose被调用的是局部变量而不是_cts,但这两者当然是指向同一个对象的,因此在功能上它确实会处理掉_cts(只要它没有被重新分配)。 - Jeroen Mostert
1
@McNets 这是一个更复杂的例子,因为 _cts 变量也可以从另一个线程更改,这就是正在检查的内容。但根本原因是相同的 - 它是为了简化 using 代码。 - Matthew Watson
@JeroenMostert 在那段代码片段中确实是这样的,但在链接页面的稍后还有另一个片段,其中 _cts 可能会被设置为与 cts 不同的值。 - Matthew Watson
1
是的,这段代码既优雅又丑陋,因为它是一个习语的简洁表达,一旦你理解了它,它就很有意义,但即使你理解了它,它看起来仍然不直观。 - Jeroen Mostert
是的,它绝对符合惯用语! - Matthew Watson
哦,好的。我现在明白了。谢谢。 - brent.reynolds

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接