2 ms·
> or ship Task<T> as class instead of struct like C# Required reading: https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetas
by DaiPlusPlus 5y ago
> or ship Task<T> as class instead of struct like C#
Required reading: https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/ https://devblogs.microsoft.com/dotnet/understanding-the-whys...
------
This isn't as-big a problem as you'd think: The CLR has internal magic that will memoize/intern certain common Task<T> results, e.g. `Task<Int32>` for Result values between 0 and 100, for example. And a compiler-generated async method that returns `Task` will use the `static Task.CompletedTask` singleton instance.
While the runtime cost of GC heap allocations is now coming under scrutiny as the current main performance factor, we shouldn't be irrationally averse to Gen 0 allocations on the order of hundreds of objects per second: that's all well-within the means of a computer made in the past 10 years, and when you use a modern concurrent background GC with a CPU with more than a couple of cores (i.e. literally every machine made in the past 7-8 years) it's nothing to worry about.
Remember that a Task<T> is used to represent a relatively expensive operation, such as IO or a background thread job, - and in the course of the computer completing that IO or job it's going to do many things orders-and-orders of magnitude more expensive than allocating a small Gen 0 object for the Task<T>.
There is a struct equivalent of Task<T> as well: ValueTask<T>, which is intended for high-throughput async operations. Of course using ValueTask<T> only makes sense when T itself is also a value-type, and what kind of practical async operations need to return hundreds-of-thousands of value-type values in an async loop? Very, very few, in my estimation. The official guidance I've read is that the people who think they "need" ValueTask<T> probably don't need it.
Also, given that a Task<T> is essentially a mutable view of some linear process it means that Task<T> needs to be a reference-type so the latest state is directly accessible to the Task<T>'s consumers. A value-type obviously cannot do that, which is why ValueTask<T> can only be `await`ed once otherwise it throws.