Hi,
I'm not able to understand the whole point of this code example for the standalone discard feature:
using System;
using System.Threading.Tasks;
public class Example
{
public static void Main()
{
ExecuteAsyncMethods().Wait();
}
private static async Task ExecuteAsyncMethods()
{
Console.WriteLine("About to launch a task...");
_ = Task.Run(() => { var iterations = 0;
for (int ctr = 0; ctr < int.MaxValue; ctr++)
iterations++;
Console.WriteLine("Completed looping operation...");
throw new InvalidOperationException();
});
await Task.Delay(5000);
Console.WriteLine("Exiting after 5 second delay");
}
}
// The example displays output like the following:
// About to launch a task...
// Completed looping operation...
// Exiting after 5 second delay
I can't understand the usefulness of the discard assignment, in the sense that based on my understanding the code behavior doesn't change even if the discard assignment is removed. The only difference is that, when the discard assignment is removed, a compiler warning pops up because inside the async function ExecuteAsyncMethods there is a task which is not awaited. Apart from that, I can't see any difference in the runtime behavior.
The following sentence which is used to explain the example it's even more confusing:
The following example uses a standalone discard to ignore the Task object returned by an asynchronous operation. This has the effect of suppressing the exception that the operation throws as it is about to complete.
Based on my knowledge of tasks and exceptions the exception raised inside Task.Run is ignored by the CLR because the task is not awaited in any way, so the exception is abandoned and the process doesn't crash. This is not related with the assignment to a discard expression, instead it's the default behavior of tasks since .NET 4.5.
⚠Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.
Thanks for pointing this out @EnricoMassoneDeltatre This is not a great example. I’ve added it to our backlog to address.
Thanks for pointing this out @EnricoMassoneDeltatre This is not a great example. I’ve added it to our backlog to address.
You are welcome. Thank you for the effort in maintaining a clear and comprehensive documentation.
@EnricoMassoneDeltatre Why did you close this issue?
@EnricoMassoneDeltatre Why did you close this issue?
Because @BillWagner said that Microsoft has added a new item in their backlog in order to improve the docs
He added this issue to the backlog. As you can see in the status update just before his comment. If you close it, they won't see it any more in their backlog.
He added _this issue_ to the backlog. As you can see in the status update just before his comment. If you close it, they won't see it any more in their backlog.
Apologize, I didn't know the relation between the github issues and the backlog.
My guess was that once an item has been added to the backlog, then the related issue can be closed without side effects of any sort on the backlog itself.
Thanks for making me aware of that. I'll leave the issue open until the new documentation for the subject will be published.
It'd be nice to have a second example that shows some other SomethingAsync method that returns some other type of Task
In addition to having more detailed information on how the behavior of exceptions work when using discard from async methods. Especially if that method call is also wrapped in a try/catch block.
Thanks!
For reference, this is a real-world example/discussion/debate we were having on this exact topic: https://github.com/windows-toolkit/WindowsCommunityToolkit/pull/2895/files#r276820784
I would also like some clarification on standalone discards' purpose. I would argue that simply dropping an expression value is actually cleaner than a discard, and less jarring to new C# developers. Moreover, many popular NuGet packages use Fluent APIs, which, by design, always have a return value to facilitate method chaining, but the final result may not actually be important. Consider this example using Moq (similar to what led me here in the first place):
var mock = new Mock<MyClass>();
mock
.Setup(x => x.Method())
.Callback(() => Console.WriteLine("test"))
.Returns(false);
The Returns method technically returns IReturnsResult<MyClass> here, but who cares? The method chain is done. In a large code base with many unit tests using Moq, my options are to add _ = before every mock.Setup chain to satisfy the compiler, suppress this warning across the codebase (which isn't ideal because discards _are_ worth being reminded of in other cases), or ignore the warning and endure blocks of green squigglies throughout the unit test code. Neither option feels great, so I think the docs should better explain what is being gained with standalone discards, vs simply dropping an expression's value.
Most helpful comment
Thanks for pointing this out @EnricoMassoneDeltatre This is not a great example. I’ve added it to our backlog to address.