I am reading the source code of Interactive Extensions and have found a line that I cannot understand:
public static Task<bool> UsingEnumerator(this Task<bool> task, IDisposable disposable)
{
task.ContinueWith(t =>
{
if (t.IsFaulted)
{
var ignored = t.Exception; // don't remove!
}
if (t.IsFaulted || t.IsCanceled || !t.Result)
disposable.Dispose();
}, TaskContinuationOptions.ExecuteSynchronously);
return task;
}
I also do not see any relevant remarks in the docs for IsFaulted
or Exception
properties.
Why this line var ignored = t.Exception; // don't remove!
is needed in this context?
A related question: I thought that such lines are optimized away in the Release mode, but given the comment and intent here that is not the case (if the code is correct). So why does this line stay in the Release mode?
That line is the difference between an observed exception and an unobserved one.
In .Net 4.0 a task with an unobserved exception will throw an UnobservedTaskException
and tear down the entire application:
"If you do not wait on a task that propagates an exception, or access its Exception property, the exception is escalated according to the .NET exception policy when the task is garbage-collected."
From Exception Handling (Task Parallel Library)
That was changed in .Net 4.5 with async-await
, although you can get the old behavior back using the app.config (<ThrowUnobservedTaskExceptions enabled="true"/>
).
There's also an event (TaskScheduler.UnobservedTaskException
) that allows you to handle such faulted tasks before the application crashes. That event is still being raised in .Net 4.5 and above.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With