163

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 '<null>' cannot be used as a default parameter because there are no standard conversions to type 'System.Threading.CancellationToken'

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?

Pang
  • 9,564
  • 146
  • 81
  • 122
tofutim
  • 22,664
  • 20
  • 87
  • 148
  • 1
    I've also seen `new CancellationToken()` which is exactly equivalent to `default` as CancellationToken is a struct. – Palec May 13 '21 at 11:27
  • Why would anybody want this? it is easy to forget to pass `CancelationToken` even if you have it in the calling method, on the other hand it is not that hard to type `CancellationToken.None` in case you dont have it? – Alexei Sosin Jun 09 '23 at 09:36

5 Answers5

247

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.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
tofutim
  • 22,664
  • 20
  • 87
  • 148
  • 4
    This is exactly what the [framework *currently* does](http://referencesource.microsoft.com/#mscorlib/system/threading/CancellationToken.cs#ec32c41597007382) internally, but I would *not* do it in my code. Think what happens with your code if Microsoft change their implementation, and `CancellationToken.None` becomes something more than `default(CancellationToken)`. – noseratio Mar 12 '14 at 22:39
  • 22
    @Noseratio That would break backwards compatibility in a big way, so I wouldn't expect that to happen. And what else would `default(CancellationToken)` do? – svick Mar 13 '14 at 12:48
  • @svick, if my library references `System.Threading.CancellationToken.None` property, and the value of `CancellationToken.None` changes in a future version of mscorlib.dll, how would that break my library (the IL code)? I would not make predictions about `default(CancellationToken)` though. – noseratio Mar 13 '14 at 12:52
  • @Noseratio If your library uses `default(CancellationToken)` (or `new CancellationToken()`), which I think is a valid thing to do for any value type, and that value started behaving differently, then that would break compatibility. – svick Mar 13 '14 at 12:56
  • @svick, I see your point, but nowhere on MSDN it is mentioned that `CancellationToken.None` *is* the same thing as `default(CancellationToken)`. Technically it may become a `static readonly` struct with non-default fields, unlikely but possible. Then if my library used `CancellationToken.None`, I'd stay compatible. Not so if it used `default(CancellationToken)`. – noseratio Mar 13 '14 at 13:55
  • 4
    @Noseratio: You're being too rigid. Chances are `CancellationToken.None` will become de facto deprecated. Even Microsoft is using `default(CancellationToken)` instead. For example, see [these search results](https://github.com/aspnet/EntityFramework/search?q=default%28CancellationToken%29) from the source code of the Entity Framework. – drowa Feb 12 '15 at 23:15
  • 25
    From MSDN [CancellationToken.None Property](https://msdn.microsoft.com/en-us/library/dd780763(v=vs.110).aspx): _"You can also use the C# default(CancellationToken) statement to create an empty cancellation token"_. None is a mistake until a futur version of C# accept it as default parameter. – MuiBienCarlota Jul 30 '15 at 09:46
  • 5
    Same [here](https://msdn.microsoft.com/en-us/library/hh873175.aspx) _To compensate for the two missing intermediate combinations, developers may pass None or a default CancellationToken for the cancellationToken parameter and null for the progress parameter._ – Arek Bal Aug 05 '15 at 11:22
28

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 ??.

Community
  • 1
  • 1
stakx - no longer contributing
  • 83,039
  • 20
  • 168
  • 268
26

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);
}
Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • 6
    This is the recommended way to handle this, as explained on the [Task-based Asynchronous Pattern](http://msdn.microsoft.com/en-us/library/hh873175.aspx) documentation on MSDN (specifically in the section **Choosing the Overloads to Provide**). – Sam Harwell Mar 12 '14 at 18:05
  • CancellationToken.None == default true – eoleary Jan 30 '18 at 18:36
  • 8
    What's the matter with `CancellationToken cancellationToken = default(CancellationToken)`? Also described here https://blogs.msdn.microsoft.com/andrewarnottms/2014/03/19/recommended-patterns-for-cancellationtoken/ – Ray Apr 03 '18 at 13:22
11

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;
    ...
}
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
9

Newer versions of C# allow for a simplified syntax for the default(CancellationToken) version. E.g.:

Task<x> DoStuff(...., CancellationToken ct = default)
Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164