Why CancellationToken is a struct?

Viewed 1029

Does it make any sense to use a struct instead of a reference type in case of CancellationToken?

I see one possible disadvantage, it will be copied all the way down in methods chain as I pass it as a parameter.

In the same time, as far as it is struct, it might be allocated and disposed faster.

If we want to make it immutable, we can use readonly properties or private setters.

So what was an idea behind it?

2 Answers

There is an article that describes the .NET Cancellation design here which is worth a read. In relation to your question, the following is asked in the comments:

Just out of interest, why is the CancellationToken a value type?

and the questioner proposes an alternative implementation with a single shared instance of CancellationToken as a reference type.

And the response by Mike Liddell:

This would certainly work and is largely equivalent (and we implemented it this way during early prototyping), but we went with the current design for two particular reasons:

– only one class instance per CTS/Token, hence less GC pressure.

– we consolidate all of the state and most of the logic onto CTS. The split arrangement was a somewhat more convoluted.

I would note that the current value type implementation is exactly the same size as a reference so there isn't any additional copying overhead. It also prevents some additional boilerplate null checks in user-code, especially when making the token an optional parameter.

Does it make any sense to use a struct instead of a reference type in case of CancellationToken?

Yes.

I see one possible disadvantage, it will be copied all the way down in methods chain as I pass it as a parameter.

That is not a disadvantage. A cancellation token is reference-sized. Why would there be a disadvantage of passing a reference-sized struct vs passing a reference? This objection doesn't make sense. Please explain why you think this is a "disadvantage".

In the same time, as far as it is struct, it might be allocated and disposed faster.

That's correct, but the actual win is more likely that a reference-sized struct that wraps a reference does not increase collection pressure. Many of the design and implementation decisions in the .NET framework are designed to ensure that collection pressure is not increased too much by framework code.

So what was an idea behind it?

It's a small type that is logically a value; why shouldn't it be a value type?

Related