I have some async code that I would like to add a CancellationToken
to. However, there are many implementations where this is not needed so I would like to have a default parameter - perhaps CancellationToken.None
. However,
Task<x> DoStuff(...., CancellationToken ct = null)
yields
A value of type '
and
Task<x> DoStuff(...., CancellationToken ct = CancellationToken.None)
Default parameter value for 'ct' must be a compile-time constant
Is there any way to have a default value for CancellationToken
?
new CancellationToken()
which is exactly equivalent to default
as CancellationToken is a struct.
It turns out that the following works:
Task<x> DoStuff(...., CancellationToken ct = default(CancellationToken))
...or:
Task<x> DoStuff(...., CancellationToken ct = default) // C# 7.1 and later
which, according to the documentation, is interpreted the same as CancellationToken.None
:
You can also use the C# default(CancellationToken) statement to create an empty cancellation token.
Is there any way to have a default value for CancellationToken?
Unfortunately, this is not possible, as CancellationToken.None
is not a compile time constant, which is a requirement for default values in optional arguments.
You can provide the same effect, however, by making an overloaded method instead of trying to use default parameters:
Task<x> DoStuff(...., CancellationToken ct)
{
//...
}
Task<x> DoStuff(....)
{
return DoStuff(...., CancellationToken.None);
}
CancellationToken cancellationToken = default(CancellationToken)
? Also described here blogs.msdn.microsoft.com/andrewarnottms/2014/03/19/…
Here are several solutions, in descending order of general goodness:
1. Using default(CancellationToken) as default value:
Task DoAsync(CancellationToken ct = default(CancellationToken)) { … }
Semantically, CancellationToken.None
would be the ideal candidate for the default, but cannot be used as such because it isn't a compile-time constant. default(CancellationToken)
is the next best thing because it is a compile-time constant and officially documented to be equivalent to CancellationToken.None
.
2. Providing a method overload without a CancellationToken parameter:
Or, if you prefer method overloads over optional parameters (see this and this question on that topic):
Task DoAsync(CancellationToken ct) { … } // actual method always requires a token
Task DoAsync() => DoAsync(CancellationToken.None); // overload producing a default token
For interface methods, the same can be achieved using extension methods:
interface IFoo
{
Task DoAsync(CancellationToken ct);
}
static class Foo
{
public static Task DoAsync(this IFoo foo) => foo.DoAsync(CancellationToken.None);
}
This results in a slimmer interface and spares implementers from explicitly writing the forwarding method overload.
3. Making the parameter nullable and using null as default value:
Task DoAsync(…, CancellationToken? ct = null)
{
… ct ?? CancellationToken.None …
}
I like this solution least because nullable types come with a small runtime overhead, and references to the cancellation token become more verbose because of the null coalescing operator ??
.
Another option is to use a Nullable<CancellationToken>
parameter, default it to null
, and deal with it inside the method:
Task<x> DoStuff(...., CancellationToken? ct = null) {
var token = ct ?? CancellationToken.None;
...
}
Newer versions of C# allow for a simplified syntax for the default(CancellationToken) version. E.g.:
Task<x> DoStuff(...., CancellationToken ct = default)
Success story sharing
CancellationToken.None
becomes something more thandefault(CancellationToken)
.default(CancellationToken)
do?CancellationToken.None
will become de facto deprecated. Even Microsoft is usingdefault(CancellationToken)
instead. For example, see these search results from the source code of the Entity Framework.