Home > Net >  Should a function allowed to throw OperationCanceledException if the passed token was CancellationTo
Should a function allowed to throw OperationCanceledException if the passed token was CancellationTo

Time:08-24

Note: a lot of C# methods now take an optional cancelation token parameter, with a default value of default that typically means same as CancellationToken.None when not supplied. But in case there is no default method parameter value, or the default is interpreted to mean something different, please also consider that someone could try to pass CancellationToken.None explicity.

Is there a design guideline around whether those methods should be allowed to throw an OperationCanceledException even though nobody actually passed them a token which will be set to canceled state? What should this guideline be?

[Consequently, is the caller going to have to think about whether to write the catch (OperationCanceledException) {} regardless of whether they pass a token or not? If so, what is the point of having the default behavior mean no cancelation, even though it sounds like a negative factor for usability?]

CodePudding user response:

what is the point of having the default behavior mean no cancelation...?

The default argument is a cancellation token that will never be cancelled, because there are a lot of contexts where you don't have a cancellation token available, and there's no point making people write more code when a sane default value is available.

That doesn't always mean the default behavior means no cancellation. For example, it's possible your HttpClient has a Timeout set, but your GetAsync call doesn't provide a CancellationToken. GetAsync can still throw an OperationCanceledException (specifically a TaskCanceledException) when the request times out.

Is there a design guideline around whether those methods should be allowed to throw an OperationCanceledException even though nobody actually passed them a token which will be set to canceled state?

No. There are plenty of cases where a given method might throw one of any number of exceptions, just because one of the methods it calls threw that exception and it didn't know what to do with it. It would be problematic to say a method isn't allowed to throw a particular kind of exception unless you provide a particular type of argument.

As in the HttpClient example, there are cases where an operation could be cancelled even though your call to the method didn't provide the cancellation token that canceled it. Perhaps an application responds to certain state changes (like a particular window being closed, or a server app pool being asked to shut down), by cancelling any active async operations.

In fact, the documentation for OperationCancelledException explicitly accounts for the possibility that an operation is cancelled by something other than a cancellation token:

If the task that was canceled had an associated cancellation token, it is returned by the CancellationToken property, and the CancellationToken.IsCancellationRequested property of the token is set to true.

There would be no need for that "If", if cancellation always had to come from a token.

Consequently, is the caller going to have to think about whether to write the catch (OperationCanceledException) {} regardless of whether they pass a token or not?

This decision has very little to do with whether a method is given a CancellationToken, and much more to do with whether the calling code would know what to do with an OperationCanceledException. The usual rules for catching (or not catching) exceptions apply just as much here: it's often best just to let exceptions work their way up the call stack, until they reach a point where you can do something useful with the error.

CodePudding user response:

As a developer experienced with async methods, I would find it very unexpected for a method to throw an OperationCanceledException after passing in CancellationToken.None or default.

If, for some reason, you want to enforce that a "real" CancellationToken is passed, you should check the CanBeCanceled property of the passed token at the beginning of the method (it will be false for CancellationToken.None). If the property is false, you should throw an ArgumentException to inform the caller of the problem. I would still find this unexpected but it would be much less confusing.

  • Related