设置CancellationToken时抛出任意异常是不好的做法吗?

ant*_*tak 5 c# async-await

它是不好的做法,有我的图书馆摆脱抛出不是其他的东西方法OperationCancelledExceptionCancellationToken.IsCancelRequested检测到?

例如:

async Task<TcpClient> ConnectAsync(string host, int port, CancellationToken ct)
{
    var client = new TcpClient();
    try
    {
        using (ct.Register(client.Close, true))
        {
            await client.ConnectAsync(host, port);
        }

        // Pick up strugglers here because ct.Register() may have hosed our client
        ct.ThrowIfCancellationRequested();
    }
    catch (Exception)
    {
        client.Close();
        throw;
    }

    return client;
}
Run Code Online (Sandbox Code Playgroud)

取消后,这有可能抛出ObjectDisposedExceptionNullReferenceException取决于时间.(因为TcpClient.ConnectAsync()TcpClient.Close()同时调用时可以抛出任何一个.)

现在,我可以解决这个问题:

async Task<TcpClient> ConnectAsync(string host, int port, CancellationToken ct)
{
    var client = new TcpClient();
    try
    {
        using (ct.Register(client.Close, true))
        {
            try
            {
                await client.ConnectAsync(host, port);
            }
            catch (Exception)
            {
                // These exceptions are likely because we closed the
                // connection with ct.Register().  Convert them to
                // OperationCancelledException if that's the case
                ct.ThrowIfCancellationRequested();
                throw;
            }
        }

        // Pick up strugglers here because ct.Register() may have hosed our client
        ct.ThrowIfCancellationRequested();
    }
    catch (Exception)
    {
        client.Close();
        throw;
    }

    return client;
}
Run Code Online (Sandbox Code Playgroud)

同样在适用的调用层次结构的每一层:

async Task<TcpClient> ConnectSslStreamAsync(string host, int port, CancellationToken ct)
{
    var client = await ConnectAsync(host, port, ct);
    try
    {
        ct.ThrowIfCancellationRequested();

        var sslStream = new SslStream(client.getStream());
        using (ct.Register(sslStream.Close))
        {
            try
            {
                await sslStream.AuthenticateAsClientAsync(...);
            }
            catch (Exception)
            {
                // These exceptions are likely because we closed the
                // stream with ct.Register().  Convert them to
                // OperationCancelledException if that's the case
                ct.ThrowIfCancellationRequested();
                throw;
            }
        }

        // Pick up strugglers here because ct.Register() may have hosed our stream
        ct.ThrowIfCancellationRequested();
    }
    catch (Exception)
    {
        client.Close();
        throw;
    }

    return client;
}
Run Code Online (Sandbox Code Playgroud)

但是,实际上,所需要的只是在外层进行一次检查,这就增加了这些额外的代码.(在取消来源.)

让这些任意例外飞行是不好的做法?或者在最外面的来电者常规中忽略它们?

Sar*_*nan 2

OperationCancelledException如果请求取消,您应该抛出。当代码的使用者遇到异常时,他们将不知道这实际上是取消还是其他原因。

OperationCancelledException也就是说,如果您由于注册关闭委托而无法抛出异常,您可以尝试此处提供的方法,您将创建一个用于关闭 tcpClient 或流的任务,并验证哪个任务首先完成并采取相应的操作。