tag:blogger.com,1999:blog-53595465125448099712024-03-18T23:01:06.140-07:00Jeremy Bytesbyte-sized chunks of .NETJeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.comBlogger529125tag:blogger.com,1999:blog-5359546512544809971.post-9118881551794798582024-02-28T09:00:00.000-08:002024-02-28T09:00:00.133-08:00Continue Processing with Parallel.ForEachAsync (even when exceptions are thrown)<p>
Parallel.ForEachAsync is a very useful tool for running code in parallel. Recently, we have been exploring what happens when an exception is
thrown inside the loop:
</p>
<p></p>
<ol style="text-align: left;">
<li>
<b>
If we "await" ForEachAsync, then we get a single exception (even if
exceptions are thrown in multiple iterations of the loop).
</b>
</li>
<li>
<b>The loop short-circuits -- meaning not all items are processed.</b>
</li>
</ol>
<p>In this series of articles, we look at these issues and how to deal with them.</p>
<p></p>
<ul style="text-align: left;">
<li>
<a href="https://jeremybytes.blogspot.com/2024/02/parallelforeachasync-and-exceptions.html">Parallel.ForEachAsync and Exceptions</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2024/02/getting-multiple-exceptions-from.html">Getting Multiple Exceptions from Parallel.ForEachAsync</a>
</li>
<li>
Continue Processing with Parallel.ForEachAsync (even when exceptions are
thrown) <i>this article</i>
</li>
</ul>
<p></p>
<p>
Code samples for all articles are available here: <a href="https://github.com/jeremybytes/foreachasync-exception">https://github.com/jeremybytes/foreachasync-exception</a>.
</p>
<p>
In the last article, we saw how to deal with behavior #1 by getting all of the
available exceptions. In this article, we will look at behavior #2 --
short-circuiting. We can eliminate the short-circuiting of the loop so that
all of the items are processed, and we can collect the exceptions along the
way.
</p>
<p>Short Version:</p>
<p><span style="font-size: large;"></span></p>
<blockquote>
<span style="font-size: large;">Handle exceptions inside the body of ForEachAsync</span></blockquote>
<p></p>
<p>
<i>For slides and code samples on Parallel.ForEachAsync (and other parallel
approaches), you can take a look at the materials from my full-day workshop
on asynchronous programming: <a href="https://github.com/jeremybytes/async-workshop-2022">https://github.com/jeremybytes/async-workshop-2022</a>. (These materials use .NET 6.0. Updates for .NET 8.0 are coming in a few
months.) For announcements on public workshops, check here: <a href="https://jeremybytes.blogspot.com/p/workshops.html">https://jeremybytes.blogspot.com/p/workshops.html</a>.</i>
</p>
<h2 style="text-align: left;">Prevent Short-Circuiting</h2>
<p>
Parallel.ForEachAsync will stop processing if it encounters an unhandled
exception. This is the behavior that we've seen in our other examples. The
result is that we only process 17 of the 100 items in our loop.
</p>
<p>
Since an unhandled exception is the cause of the short-circuit, we can
continue processing by eliminating that unhandled exception.
</p>
<p>And an easy way to eliminate an unhandled exception is to handle it.</p>
<h2 style="text-align: left;">Try/Catch inside the ForEachAsync Body</h2>
<p>
Here is the updated code (from the
<a href="https://github.com/jeremybytes/foreachasync-exception/blob/main/ForEachAsyncException/doesnt-stop/Program.cs">"doesnt-stop/Program.cs" file</a>):
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXsmHtzE2KgUjduYRN73UT38Pn2JfE4UaE__IsLXckiR8UcYYJlkrHlDNh4WHoXsV2E4JTETwhkzQuU78ZsCuzDY5gV4QEjIcKLwEYlqSQS_7T0Lyxzs9TTJuD4aK2qaPYSv-CQ_kXUCoU2GJLBUUZreEQNhK2ApV-HEBfj59y8bBqhYsGxTkDrGd4QQ0/s1025/01-trycatch-inside.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="551" data-original-width="1025" height="344" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXsmHtzE2KgUjduYRN73UT38Pn2JfE4UaE__IsLXckiR8UcYYJlkrHlDNh4WHoXsV2E4JTETwhkzQuU78ZsCuzDY5gV4QEjIcKLwEYlqSQS_7T0Lyxzs9TTJuD4aK2qaPYSv-CQ_kXUCoU2GJLBUUZreEQNhK2ApV-HEBfj59y8bBqhYsGxTkDrGd4QQ0/w640-h344/01-trycatch-inside.png" width="640" /></a>
</div>
<code>
<pre> await Parallel.ForEachAsync(Enumerable.Range(1, 100),
new ParallelOptions() { MaxDegreeOfParallelism = 10 },
async (i, _) =>
{
try
{
Console.WriteLine($"Processing item: {i}");
await Task.Delay(10); // simulate async task
MightThrowException(i);
Interlocked.Increment(ref TotalProcessed);
}
catch (Exception ex)
{
Console.WriteLine($"Caught in Loop: {ex.Message}");
}
});</pre>
</code>
<p>
Here we have a try/catch block inside the body of ForEachAsync. If an
exception is thrown, it is handled inside of the loop. From ForEachAsync's
perspective, there are no unhandled exceptions, so it continues processing.
</p>
<h2 style="text-align: left;">Output</h2>
<p>
In the output, all 100 items processed -- or are at least attempted to be processed. Here is the last part
of the output:
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4POzClGn44tFDuPuG_4NdbfUIfpFrkvzDDD22wqIBeXKSKR-Js83KfYvWIL-FXVoqPhPpwZwI5z3HpN8vVoFGFGuKjObQ9bHEMhOIAiwoGXx2rvBU75epEetX1BLe7eiHroYREles9KLbCxbK_MBk_-jg60vZGe9NPPTgE9eLqE2e8ToCvvhNmR9_l38/s1033/02-output.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="757" data-original-width="1033" height="470" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4POzClGn44tFDuPuG_4NdbfUIfpFrkvzDDD22wqIBeXKSKR-Js83KfYvWIL-FXVoqPhPpwZwI5z3HpN8vVoFGFGuKjObQ9bHEMhOIAiwoGXx2rvBU75epEetX1BLe7eiHroYREles9KLbCxbK_MBk_-jg60vZGe9NPPTgE9eLqE2e8ToCvvhNmR9_l38/w640-h470/02-output.png" width="640" /></a>
</div>
<br />
<code>Caught in Loop: Bad thing happened inside loop (87)<br />
Processing item: 92<br />
Processing item: 93<br />
Processing item: 94<br />
Processing item: 95<br />
Caught in Loop: Bad thing happened inside loop (84)<br />
Processing item: 96<br />
Caught in Loop: Bad thing happened inside loop (81)<br />
Processing item: 97<br />
Processing item: 98<br />
Processing item: 99<br />
Processing item: 100<br />
Caught in Loop: Bad thing happened inside loop (99)<br />
Caught in Loop: Bad thing happened inside loop (93)<br />
Caught in Loop: Bad thing happened inside loop (96)<br />
<br />
Total Processed: 67<br />
Total Exceptions: 33<br />
Done (Doesn't Stop for Exceptions)</code>
<p>
All 100 items were processed. 67 were successful and 33 of them failed -- this
is what we expect based on our method that throws exceptions.
</p>
<h2 style="text-align: left;">Observations</h2>
<p>
With this approach, we do not have to deal with AggregateException. Instead,
we handle the individual exceptions as they occur. This could include logging
or retrying the operation.
</p>
<p>
Because we have a standard try/catch block, we get the full exception
(including stack trace and other information). We can log this information if
we need to investigate further.
</p>
<p>
Since the loop does not stop, we do not need to worry about where the loop
left in a short-circuit. All of the items have a chance to be processed.
</p><p>We do need to worry about concurrency. The body of the catch block could be running for multiple items at the same time. So we need to ensure that our logging methods and any other processing in the catch block is thread-safe.</p>
<h2 style="text-align: left;">Wrap Up</h2>
<p>
Ultimately, the approach we take depends on the specific needs of the process.
But we do need to keep the default behavior of Parallel.ForEachAsync in mind:
</p>
<ol style="text-align: left;">
<li>
<b>
If we "await" ForEachAsync, then we get a single exception (even if
exceptions are thrown in multiple iterations of the loop).
</b>
</li>
<li>
<b>The loop short-circuits -- meaning not all items are processed.</b>
</li>
</ol>
<p>If getting an exception in the ForEachAsync loop is truly exceptional (meaning it really should never happen), then the default behavior may be okay. An exception is a catastrophic error that stops the process and lets us know that (at least) one item failed. In this case, default behavior may be fine (as we saw in the <a href="https://jeremybytes.blogspot.com/2024/02/parallelforeachasync-and-exceptions.html">first article</a>).<br /></p><p>It may be that short-circuiting is okay (because we need to restart the loop from the beginning in case of failure), but we still want more information about what happened. We can get all of the available exceptions by either using a continuation or by using ConfigureAwaitOptions (as we saw in the <a href="https://jeremybytes.blogspot.com/2024/02/getting-multiple-exceptions-from.htmlhttps://jeremybytes.blogspot.com/2024/02/getting-multiple-exceptions-from.html">last article</a>).</p><p>If we want to continue processing even if some of the items fail, then we can take the approach from this article -- put a try/catch block inside the body of ForEachAsync (as we saw in this article).</p><p>Whatever approach we take, it is always good to know that there are options. Each application has its own needs. Our job as programmers is to pick an option that works well for the application. So keep looking at options; you never know when you'll need a particular one.</p><p>Happy Coding!</p>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-45065856729647287282024-02-27T09:00:00.000-08:002024-02-28T09:15:48.220-08:00Getting Multiple Exceptions from Parallel.ForEachAsync<p>
Parallel.ForEachAsync is a very useful tool for running code in parallel. Last
time, we looked at what happens when an exception is thrown inside of the
loop:
</p>
<ol style="text-align: left;">
<li>
<b>If we "await" ForEachAsync, then we get a single exception (even if
exceptions are thrown in multiple iterations of the loop).
</b>
</li>
<li>
<b>The loop short-circuits -- meaning not all items are processed.</b>
</li>
</ol>
<p>
Depending on what we are doing in the parallel loop, these items may not be a
concern. But there are situations where I would like to get all of the
exceptions back; and there are times when I would like to capture the
exceptions and continue processing.
</p>
<p>
In this series of articles, we look at these issues and how to deal with
them.
</p>
<p></p>
<ul style="text-align: left;">
<li>
<a href="https://jeremybytes.blogspot.com/2024/02/parallelforeachasync-and-exceptions.html">Parallel.ForEachAsync and Exceptions</a>
</li>
<li>
Getting Multiple Exceptions from Parallel.ForEachAsync <i>this article</i>
</li>
<li><a href="https://jeremybytes.blogspot.com/2024/02/continue-processing-with.html">Continue Processing with Parallel.ForEachAsync (even when exceptions are thrown)</a></li>
</ul>
<p></p>
<p>
Code samples for all articles are available here: <a href="https://github.com/jeremybytes/foreachasync-exception">https://github.com/jeremybytes/foreachasync-exception</a>.
</p>
<p>
In this article, we will take on the first issue: how to get all available
exceptions from the loop. The number of exceptions that we get depends on the
second issue. Since the loop short-circuits, we end up with some
exceptions, but not as many as if the loop were to complete normally. (The
third article in the series shows how we can keep the loop from
short-circuiting.)
</p>
<h2 style="text-align: left;">2 Approaches</h2>
<p>We will look at 2 approaches to getting the available exceptions.</p>
<p></p>
<ol style="text-align: left;">
<li>
<b> Using a Continuation</b><br />With this approach, we look at the
exceptions in a continuation instead of letting them bubble up through the
ForEachAsync method/Task. <br /><br />
</li>
<li>
<b> Using ConfigureAwaitOptions.SuppressThrowing</b><br />This uses a
feature added in .NET 8: ConfigureAwaitOptions. With this approach, we
suppress the exceptions in the loop and then use a "Wait" to show the
aggregate exception. I came across this approach in <b>Gérald Barré's article
"<a href="https://www.meziantou.net/getting-all-exceptions-thrown-from-parallel-foreachasync.htm">Getting all exceptions thrown from Parallel.ForEachAsync</a>"</b>.
</li>
</ol>
<p>
The approaches both give access to the entire set of exceptions. The
differences are subtle, and we'll look at those after we've seen both
approaches.
</p>
<h2 style="text-align: left;">A Reminder</h2>
<p>
As a reminder, here is how we left our code in the
<a href="https://jeremybytes.blogspot.com/2024/02/parallelforeachasync-and-exceptions.html">previous article</a> (from the
<a href="https://github.com/jeremybytes/foreachasync-exception/blob/main/ForEachAsyncException/original-code/Program.cs">original-code/Program.cs file</a>):
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO4YqF2BWCOzfQpDTVumwwG9ofUqRvjOISSWH1QbMSq3LrtOKagwxtI2lilLIdwsWvc9vcOj8A7p13NeGxkLuJuS0gOOQ1T1Fd630xjP7SY8gcdvnrivBYgqgGwKXaZqmiIaU8GU7KYj1GGJuW3TG_YkQQXcrrCW4gsLT1OK9x-zLBCEjFyUM5Hs71guU/s1010/01-starting-point.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="590" data-original-width="1010" height="374" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO4YqF2BWCOzfQpDTVumwwG9ofUqRvjOISSWH1QbMSq3LrtOKagwxtI2lilLIdwsWvc9vcOj8A7p13NeGxkLuJuS0gOOQ1T1Fd630xjP7SY8gcdvnrivBYgqgGwKXaZqmiIaU8GU7KYj1GGJuW3TG_YkQQXcrrCW4gsLT1OK9x-zLBCEjFyUM5Hs71guU/w640-h374/01-starting-point.png" width="640" /></a>
</div>
<p></p>
<code>
<pre> try
{
await Parallel.ForEachAsync(
Enumerable.Range(1, 100),
new ParallelOptions() { MaxDegreeOfParallelism = 10 },
async (i, _) =>
{
Console.WriteLine($"Processing item: {i}");
await Task.Delay(10); // simulate async task
MightThrowException(i);
Interlocked.Increment(ref TotalProcessed);
});
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}</pre>
</code>
<p>
Every 3rd iteration of the loop throws an exception. This causes the
ForEachAsync loop to short-circuit (after 17 items, generally). Because of the
"await" on ForEachAsync, only 1 of the exceptions is shown (out of 5,
generally).
</p>
<h2 style="text-align: left;">Using A Continuation</h2>
<p>
For the first approach, we will use a continuation. Instead of letting the
exceptions bubble up through the ForEachAsync method call, we add a
continuation that runs some code if there is an exception. From there, we can gracefully
handle the exception(s).
</p>
<p>
Here is the code for that (from the
<a href="https://github.com/jeremybytes/foreachasync-exception/blob/main/ForEachAsyncException/continuation/Program.cs">"continuation/Program.cs" file</a>):
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIaLvlKbURGWVUuJIbgwbaXnoAVDojHBKcuW1C7P_MGpLAXlRmWKeKl1aL3uHgsfGrLUhekWXTlGCU3-XdI8R-U6zZZWN3G4IL511CAiuGCTeuDUHzgNnSnPkSkVYHw4xwITBoHORwj534D6puPByX6p_CKlw6yi8g-pG4HosBDA0PdXjFkEeSuk5CJEE/s1212/02-continuation.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="590" data-original-width="1212" height="312" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIaLvlKbURGWVUuJIbgwbaXnoAVDojHBKcuW1C7P_MGpLAXlRmWKeKl1aL3uHgsfGrLUhekWXTlGCU3-XdI8R-U6zZZWN3G4IL511CAiuGCTeuDUHzgNnSnPkSkVYHw4xwITBoHORwj534D6puPByX6p_CKlw6yi8g-pG4HosBDA0PdXjFkEeSuk5CJEE/w640-h312/02-continuation.png" width="640" /></a>
</div>
<p></p>
<code>
<pre> try
{
await Parallel.ForEachAsync(Enumerable.Range(1, 100),
new ParallelOptions() { MaxDegreeOfParallelism = 10 },
async (i, _) =>
{
Console.WriteLine($"Processing item: {i}");
await Task.Delay(10); // simulate async task
MightThrowException(i);
Interlocked.Increment(ref TotalProcessed);
})
.ContinueWith(task =>
{
if (task.IsFaulted)
Console.WriteLine($"Exception: {task.Exception!.Message}");
});
}</pre>
</code>
<p>
After calling "Parallel.ForEachAsync", we add a continuation by calling
".ContinueWith". This specifies code that we want to run after a Task has
completed.
</p>
<p>
The parameter of "ContinueWith" is a delegate that has the code we want to run
when the Task is complete. The "task" parameter represents the ForEachAsync
task itself.
</p>
<p>
Because we have access to the this task, we can check the "IsFaulted"
property. A task is faulted if an exception is thrown in the task (which is exactly what
we are expecting here). If an exception is thrown, then we output it to the
console.
</p>
<h3 style="text-align: left;">Output</h3>
<p>When we run the code, we get the following output:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhvxwfCFycIzfh91Tid-itoOrFsnmRpbyMXBjQXc7PVlFBUPxuFyUV8zzhSFacwPn78DU35d2sY2CplBktHklJp9bThugaJj19yI3tBeApscVvjcTesZVshqywNMQuBXPK-_i89-GOO7I98O22SwGHUSBvcgnygYIH_6MeNBeBbP4LfY0RbFR_8NLFmUs/s1232/04-output1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1002" data-original-width="1232" height="520" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhvxwfCFycIzfh91Tid-itoOrFsnmRpbyMXBjQXc7PVlFBUPxuFyUV8zzhSFacwPn78DU35d2sY2CplBktHklJp9bThugaJj19yI3tBeApscVvjcTesZVshqywNMQuBXPK-_i89-GOO7I98O22SwGHUSBvcgnygYIH_6MeNBeBbP4LfY0RbFR_8NLFmUs/w640-h520/04-output1.png" width="640" /></a>
</div>
<br />
<code>Processing item: 9<br />
Processing item: 4<br />
Processing item: 1<br />
Processing item: 6<br />
Processing item: 5<br />
Processing item: 8<br />
Processing item: 3<br />
Processing item: 10<br />
Processing item: 2<br />
Processing item: 7<br />
Processing item: 11<br />
Processing item: 16<br />
Processing item: 12<br />
Processing item: 13<br />
Processing item: 14<br />
Processing item: 15<br />
Processing item: 17<br />
Exception: One or more errors occurred. (Bad thing happened inside loop (6))
(Bad thing happened inside loop (3)) (Bad thing happened inside loop (9)) (Bad
thing happened inside loop (15)) (Bad thing happened inside loop (12))<br />
<br />
Total Processed: 12<br />
Total Exceptions: 5<br />
Done (AggregateException from Continuation)</code>
<p>
This shows us the 17 items that were processed, and the Exception message "One
or more errors occurred" along with additional information. This is a typical
message from an AggregateException.
</p>
<p>
<i>For more specifics on AggregateException, you can take a look at "<a href="https://jeremybytes.blogspot.com/2015/01/task-and-await-basic-exception-handling.html">Task and Await: Basic Exception Handling</a>" and "<a href="https://jeremybytes.blogspot.com/2020/09/await-taskwhenall-shows-one-exception.html">'await Task.WhenAll' Shows One Exception - Here's How to See Them All</a>".</i>
</p>
<p>
An AggregateException contains all of the exceptions that were thrown in a
Task. It has a tree structure of inner exceptions that let it handle various
complexities of Task (including concurrent tasks and child tasks).
</p>
<h3 style="text-align: left;">The Original Catch Block Does Nothing</h3>
<p>
The call to Parallel.ForEachAsync is inside a "try" block (just like in our
original code). There is still a "catch" block, but it is not used in this
scenario.
</p>
<p>Here's the updated "catch" block:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW8r49coCypZTq6x6AJEzm-maVKFwxnuPNj6gyAz8Q1zFvQBksZb-u5VvMGA0knSl38E_8IrCrhMsHW5FNDF8MDpbflZt6IqlCVbZWP4gWx93ntAf0PpaMaFQNDgDvUeG827o8BAm8UTRYoUzCvS-_LUEf-bwoA1LvA6YsXb0xqd3iRN-Wok1_knMYCEw/s927/03-unused-catch.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="312" data-original-width="927" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW8r49coCypZTq6x6AJEzm-maVKFwxnuPNj6gyAz8Q1zFvQBksZb-u5VvMGA0knSl38E_8IrCrhMsHW5FNDF8MDpbflZt6IqlCVbZWP4gWx93ntAf0PpaMaFQNDgDvUeG827o8BAm8UTRYoUzCvS-_LUEf-bwoA1LvA6YsXb0xqd3iRN-Wok1_knMYCEw/w640-h216/03-unused-catch.png" width="640" /></a>
</div>
<code>
<pre> catch (Exception)
{
// You never get to this code. Exceptions are handled
// inside the ForEachAsync loop.
// But just in case, rethrow the exception
Console.WriteLine("You shouldn't get here");
throw;
}</pre>
</code>
<p>
As the comment says, we should never get to this catch block. But just in case
we do, we rethrow the exception. In this case, it would result in an unhandled
exception and the application would crash.
</p>
<p>
<b>But why won't we get to this "catch" block?</b><br />Our code has changed
in a subtle way. In the original code, we used "await" on the ForEachAsync
method/Task. When we "await" a faulted Task, the AggregateException is not
thrown; instead, one of the inner exceptions is thrown. (This is what we saw in
the
<a href="https://jeremybytes.blogspot.com/2024/02/parallelforeachasync-and-exceptions.html">previous article</a>.)
</p>
<p>
The difference here is that we no longer "await" the ForEachAsync method.
Instead, we "await" the continuation task returned from "ContinueWith". The
code in our continuation is not likely to throw an exception, so we will not
hit the "catch" block.
</p>
<h3 style="text-align: left;">Displaying the Inner Exceptions</h3>
<p>
So we now have access to the AggregateException. Let's display the inner
exceptions. Here is a method to do that (from the
<a href="https://github.com/jeremybytes/foreachasync-exception/blob/main/ForEachAsyncException/continuation/Program.cs">"continuation/Program.cs" file</a>):
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtjf6U9sHSiK4YbN5iAyKzeJukojG_bYe7MiEf2V-Tvp005-q4iZbsp2S4Bp0dnS-Cmdvx1mc4KgLBaKLWUMv8g64GOOibuTmNfY2eE8Cs9s8D_DTtCwbz9n57qBmHJqp9dmi6UXukoxX1Ca_JdKyRR60bNQAH7VWbCglfBrLoGflOEuijQp8RZ7GabgY/s1265/05-showaggregate.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="483" data-original-width="1265" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtjf6U9sHSiK4YbN5iAyKzeJukojG_bYe7MiEf2V-Tvp005-q4iZbsp2S4Bp0dnS-Cmdvx1mc4KgLBaKLWUMv8g64GOOibuTmNfY2eE8Cs9s8D_DTtCwbz9n57qBmHJqp9dmi6UXukoxX1Ca_JdKyRR60bNQAH7VWbCglfBrLoGflOEuijQp8RZ7GabgY/w640-h244/05-showaggregate.png" width="640" /></a>
</div>
<code>
<pre> private static void ShowAggregateException(AggregateException ex)
{
StringBuilder builder = new();
var innerExceptions = ex.Flatten().InnerExceptions;
builder.AppendLine("======================");
builder.AppendLine($"Aggregate Exception: Count {innerExceptions.Count}");
foreach (var inner in innerExceptions)
builder.AppendLine($"Continuation Exception: {inner!.Message}");
builder.Append("======================");
Console.WriteLine(builder.ToString());
}</pre>
</code>
<p>
This flattens the inner exceptions (to get rid of the tree structure), loops
through each exception, and builds a string to display on the console.
</p>
<p>We just need to adjust our continuation to call this new method:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0zD6kb67yP7NwQVt0TFV2TjnjQHKbodLS82BMHnyQJ-1x9wCnfZT-bYVac-ubuce1tsn_gwsA803qMMV5Q14N-tfgRbNUz81Po3YBGfesufEzRaHJGJeSvOE79_zX0pphlQyO5473ynOPbxxEXpMN1GsSDF8ZFnuN7g54QfcSpUul3TKzkcSM06KUz1U/s1003/06-with-showaggregate.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="587" data-original-width="1003" height="374" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0zD6kb67yP7NwQVt0TFV2TjnjQHKbodLS82BMHnyQJ-1x9wCnfZT-bYVac-ubuce1tsn_gwsA803qMMV5Q14N-tfgRbNUz81Po3YBGfesufEzRaHJGJeSvOE79_zX0pphlQyO5473ynOPbxxEXpMN1GsSDF8ZFnuN7g54QfcSpUul3TKzkcSM06KUz1U/w640-h374/06-with-showaggregate.png" width="640" /></a>
</div>
<code>
<pre> .ContinueWith(task =>
{
if (task.IsFaulted)
ShowAggregateException(task.Exception);
});</pre>
</code>
<p>Now we get the following output:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb4XSotilyksmqytmuNque_mFlzuVAFRfKs20UozthZundCAfFrnCdmsnEVybJAs1ppgbOjpXrgKkZdPmLd-hQWZZ8xBXaHTStyxFBNWLZ_oJZKGSIXPFBsurnaSSjHgrGlWUiiYaWrxcaNC153YdaH68S1IFdR9VzUoNIrF13yv2c1zIf5ZPv81uwfMQ/s1200/07-output2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="920" data-original-width="1200" height="490" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb4XSotilyksmqytmuNque_mFlzuVAFRfKs20UozthZundCAfFrnCdmsnEVybJAs1ppgbOjpXrgKkZdPmLd-hQWZZ8xBXaHTStyxFBNWLZ_oJZKGSIXPFBsurnaSSjHgrGlWUiiYaWrxcaNC153YdaH68S1IFdR9VzUoNIrF13yv2c1zIf5ZPv81uwfMQ/w640-h490/07-output2.png" width="640" /></a>
</div>
<code>
<pre>Processing item: 9
Processing item: 2
Processing item: 6
Processing item: 1
Processing item: 3
Processing item: 8
Processing item: 7
Processing item: 4
Processing item: 10
Processing item: 5
Processing item: 11
Processing item: 12
Processing item: 16
Processing item: 14
Processing item: 15
Processing item: 13
Processing item: 17
======================
Aggregate Exception: Count 5
Continuation Exception: Bad thing happened inside loop (6)
Continuation Exception: Bad thing happened inside loop (3)
Continuation Exception: Bad thing happened inside loop (9)
Continuation Exception: Bad thing happened inside loop (12)
Continuation Exception: Bad thing happened inside loop (15)
======================
Total Processed: 12
Total Exceptions: 5
Done (AggregateException from Continuation)</pre>
</code>
<p>
Now we can see all 5 of the exceptions that were thrown in our loop. In this
case, we are simply putting the exception message on the console. But we do
have access to each of the full exceptions, including the stack traces. So we
have the access to the same information as if we were to "catch" them.
</p>
<p>
This shows all of the exceptions that were thrown by using a continuation. Now
let's look at another way to get the exceptions.
</p>
<h2 style="text-align: left;">Using ConfigureAwaitOptions.SuppressThrowing</h2>
<p>
I came across this approach in <b>Gérald Barré's article "<a href="https://www.meziantou.net/getting-all-exceptions-thrown-from-parallel-foreachasync.htm">Getting all exceptions thrown from Parallel.ForEachAsync</a>"</b>. You can read his original article for more details.
</p>
<p>
This code is in the
<a href="https://github.com/jeremybytes/foreachasync-exception/tree/main/ForEachAsyncException/configure-await-options">"configure-await-options" project</a>
in the code repository.
</p>
<h3 style="text-align: left;">An Extension Method</h3>
<p>
This approach uses an extension method to suppress throwing exceptions on the
awaited task (to keep the AggregateException from getting unwrapped). Then it waits on the Task to get the AggregateException directly.
</p>
<p>
<i>Note: This uses a feature added in .NET 8: ConfigureAwaitOptions. So, this
approach will not work for earlier versions of .NET.</i>
</p>
<p>
Here is the extension method (from the
<a href="https://github.com/jeremybytes/foreachasync-exception/blob/main/ForEachAsyncException/configure-await-options/Program.cs">"configure-await-options/Program.cs" file</a>):
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgj6rnMJAzQrKLc4UvEtBGn84NRed2V3heMxalUGCudmCPgw0NS1Z7tkpucgO8CISdP46QN9m086PDBaSO-wrGJVPxPZtrYF22i8hW2HAMo-ItjRiv0-_RIJezNuCfp4T7tCk6p4Gq_3knw2Yzu_JTAGMIS-D5V4sEQFoH55sVwkGA1oUBX64aTJps_3YA/s1196/08-extension-method.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="263" data-original-width="1196" height="140" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgj6rnMJAzQrKLc4UvEtBGn84NRed2V3heMxalUGCudmCPgw0NS1Z7tkpucgO8CISdP46QN9m086PDBaSO-wrGJVPxPZtrYF22i8hW2HAMo-ItjRiv0-_RIJezNuCfp4T7tCk6p4Gq_3knw2Yzu_JTAGMIS-D5V4sEQFoH55sVwkGA1oUBX64aTJps_3YA/w640-h140/08-extension-method.png" width="640" /></a>
</div>
<code>
<pre>public static class Aggregate
{
internal static async Task WithAggregateException(this Task task)
{
await task.ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing);
task.Wait();
}
}</pre>
</code>
<p>
This extension method takes a Task as a parameter and returns a Task. So we
can think of this as a decorator of sorts -- it modifies the functionality of
a Task in a fairly transparent way to the caller.
</p>
<p>At the heart of this is the "ConfigureAwait" call. "ConfigureAwait" got a new parameter in .NET 8: ConfigureAwaitOptions. If you
want the details, I would recommend reading <b>Stephen Cleary's article:
<a href="https://blog.stephencleary.com/2023/11/configureawait-in-net-8.html">ConfigureAwait in .NET 8</a></b>.
</p>
<p>
In this code, "SuppressThrowing" will keep the Task from throwing an exception
when it is awaited. If we "await" a task that has this option set, an
exception will <b>not</b> be thrown at the "await".
</p>
<p>
The next line of the method calls "Wait" on the task. Generally, we want to
avoid "Wait" because it is a blocking operation. But since we call "Wait" on a
task that has already been awaited, we do not need to worry about this. The
task is already complete.
</p>
<p>
But when we call "Wait" on a faulted task, the exception is thrown. And in
this case, it is the full AggregateException (rather than the unwrapped inner
exception that we normally get with "await").
</p>
<h3 style="text-align: left;">Using the Extension Method</h3>
<p>
To use the extension method, we tack it onto the end of the
Parallel.ForEachAsync call (also in the
<a href="https://github.com/jeremybytes/foreachasync-exception/blob/main/ForEachAsyncException/configure-await-options/Program.cs">"configure-await-options/Program.cs" file</a>):
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4MuI_Uu_V7bjgzB0a_1THVQ_fkMpin3VDEjeQeT_vslzWLwl0Vw81iW7g8pKpvPWe4owUuTsZisQcLDaAl6SOs049HDAaOmjujXRHjzQJHL6xdfdIyKoymKN1u9fX2yRpu2CKrbSfjep3gNsrYcGRVH84HybxXjshIiFafWBuuuLptDEDdfRoVgg7Djc/s1006/09-using-extension-method.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="730" data-original-width="1006" height="464" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4MuI_Uu_V7bjgzB0a_1THVQ_fkMpin3VDEjeQeT_vslzWLwl0Vw81iW7g8pKpvPWe4owUuTsZisQcLDaAl6SOs049HDAaOmjujXRHjzQJHL6xdfdIyKoymKN1u9fX2yRpu2CKrbSfjep3gNsrYcGRVH84HybxXjshIiFafWBuuuLptDEDdfRoVgg7Djc/w640-h464/09-using-extension-method.png" width="640" /></a>
</div>
<code>
<pre> try
{
await Parallel.ForEachAsync(Enumerable.Range(1, 100),
new ParallelOptions() { MaxDegreeOfParallelism = 10 },
async (i, _) =>
{
Console.WriteLine($"Processing item: {i}");
await Task.Delay(10); // simulate async task
MightThrowException(i);
Interlocked.Increment(ref TotalProcessed);
}).WithAggregateException();
}
catch (AggregateException ex)
{
ShowAggregateException(ex);
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}</pre>
</code>
<p>
Notice that "WithAggregateException" is called on the "ForEachAsync" task.
This means that the Task that is awaited at the top is the Task that has been
modified by the extension method.
</p>
<p>So instead of throwing an individual exception, this throws an AggregateException.
</p>
<p>
You can see that we have an additional "catch" block for AggregateException,
and this uses the same "ShowAggregateException" method from the other example.
</p>
<h3 style="text-align: left;">Output</h3>
<p>The output is similar to the other example:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1fx1HU0iupVJXppJIBw31f7d7hEUbotCmmx67cObcNjePGSSsoFf2GzxM8L87tKbdi4uReFn8-_Fr8mp23BI6oOZAHvPHdlPctJWrA16z8YsVGmOZgoFq0uJI5d97EYPWoWwDQV9Q4xo6yCW3FejikCMhuyfBGFy1Bhik0C8D6LPMOMgZIwB3DjvPwlQ/s902/10-output.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="876" data-original-width="902" height="622" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1fx1HU0iupVJXppJIBw31f7d7hEUbotCmmx67cObcNjePGSSsoFf2GzxM8L87tKbdi4uReFn8-_Fr8mp23BI6oOZAHvPHdlPctJWrA16z8YsVGmOZgoFq0uJI5d97EYPWoWwDQV9Q4xo6yCW3FejikCMhuyfBGFy1Bhik0C8D6LPMOMgZIwB3DjvPwlQ/w640-h622/10-output.png" width="640" /></a>
</div>
<code>
<pre>Processing item: 10
Processing item: 7
Processing item: 9
Processing item: 2
Processing item: 4
Processing item: 6
Processing item: 8
Processing item: 5
Processing item: 1
Processing item: 3
Processing item: 11
Processing item: 15
Processing item: 12
Processing item: 13
Processing item: 14
Processing item: 16
Processing item: 17
======================
Aggregate Exception: Count 5
Continuation Exception: Bad thing happened inside loop (6)
Continuation Exception: Bad thing happened inside loop (9)
Continuation Exception: Bad thing happened inside loop (3)
Continuation Exception: Bad thing happened inside loop (12)
Continuation Exception: Bad thing happened inside loop (15)
======================
Total Processed: 12
Total Exceptions: 5
Done (AggregateException from ConfigureAwaitOptions)</pre>
</code>
<p>
Now we can see all 5 of the exceptions that were thrown in our loop. As with
the first example, we have access to each of the full exceptions, including
the stack traces.
</p>
<h2 style="text-align: left;">Differences in the Approaches</h2>
<p>
These approaches both accomplish the same thing. They give us access to all of
the exceptions that were thrown inside the ForEachAsync loop. Here are some
things to keep in mind.
</p>
<h3 style="text-align: left;">Availability</h3>
<p>
<b>Using a Continuation </b>is available back to .NET 6 (technically, it goes
back further, but ForEachAsync only goes back to .NET 6, so that's really what
we care about).
</p>
<p>
<b>ConfigureAwaitOptions</b> is new in .NET 8. So this will work great going
forward, but does not work with prior versions of .NET.
</p>
<p>
Which approach you prefer depends on what version of .NET you use. If your
code is .NET 8, then either approach will work.
</p>
<h3 style="text-align: left;">Throwing Exceptions</h3>
<p>
<b>Using a Continuation </b>does not throw the AggregateException. Instead,
the AggregateException is cracked open and examined in the continuation. This
is also why we do not hit the "catch" block that we have in the code.<br /><i>Note: With this approach, the outer AggregateException is never thrown, so
we do not get the stack trace and some other elements that are filled in
when an exception is thrown. All of the inner exceptions (the ones we care
about here) *have* been thrown, so they do have the stack trace and other
information.</i>
</p>
<p>
<b>ConfigureAwaitOptions </b>does throw the AggregateException. When
"task.Wait()" is called, the AggregateException is thrown. This is caught in
the appropriate "catch" block where we can examine it.
</p>
<p>
Which approach you prefer will depend on your philosophy when it comes to
exceptions and exception handling.
</p>
<p>
Throwing an exception is fairly expensive computationally. So some developers
try to avoid throwing exceptions if processing can be handled another way.
</p>
<p>
On the other hand, some developers prefer to throw exceptions so that they can
follow a consistent try/catch pattern throughout their code. This gives
consistency and takes advantage of the chain-of-responsibility set up by the
exception handling mechanism.
</p>
<h3 style="text-align: left;">A Missing Feature</h3>
<p>
Both of these approaches give us access to all of the exceptions that were
thrown. However, they do not address the short-circuiting problem. Even though
our loop is set up to process 100 items, it stops processing after 17. As
noted in the previous article, this is due to the way that
Parallel.ForEachAsync is designed.
</p>
<p>
This missing feature may or may not be an issue, depending on what your code
is trying to do.
</p>
<h3 style="text-align: left;">Bottom Line</h3>
<p>
Both of these approaches accomplish our mission of getting all of the
exceptions thrown in Parallel.ForEachAsync.
</p>
<h2 style="text-align: left;">Wrap Up</h2>
<p>
Once we start diving into exception handling, we find that there are different
options and different approaches. This includes addressing the default behavior of Parallel.ForEachAsync:</p>
<ol style="text-align: left;"><li><b>If we "await" ForEachAsync, then we get a single exception (even if
exceptions are thrown in multiple iterations of the loop).
</b>
</li>
<li>
<b>The loop short-circuits -- meaning not all items are processed.</b>
</li>
</ol>
<p></p><ol style="-webkit-text-stroke-width: 0px; color: black; font-family: "Times New Roman"; font-size: medium; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;"></ol><p></p><p style="-webkit-text-stroke-width: 0px; color: black; font-family: "Times New Roman"; font-size: medium; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;">With both of the approaches shown in this article, we have solved for behavior #1: we get access to the AggregateException of the ForEachAsync task. With this information, we can find all of the errors that occurred in the loop.</p><p style="-webkit-text-stroke-width: 0px; color: black; font-family: "Times New Roman"; font-size: medium; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;">If this is all that we are concerned about, then we are good to go. But if we are concerned about item #2 (the loop short-circuiting), we need to look at another approach. And that is what we will do in the <a href="https://jeremybytes.blogspot.com/2024/02/continue-processing-with.html">next article</a>.</p><p style="-webkit-text-stroke-width: 0px; color: black; font-family: "Times New Roman"; font-size: medium; font-style: normal; font-variant-caps: normal; font-variant-ligatures: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: left; text-decoration-color: initial; text-decoration-style: initial; text-decoration-thickness: initial; text-indent: 0px; text-transform: none; white-space: normal; widows: 2; word-spacing: 0px;">Happy Coding!</p>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-50361078276646168652024-02-26T13:30:00.000-08:002024-02-28T09:15:05.448-08:00Parallel.ForEachAsync and Exceptions<p>
Parallel.ForEachAsync is a very useful tool for running code in parallel. But
what happens when an exception is thrown inside the loop?
</p>
<p>By default, the following things happen:</p>
<p></p>
<ol style="text-align: left;">
<li>
<b>
If we "await" ForEachAsync, then we get a single exception (even if
exceptions are thrown in multiple iterations of the loop).
</b>
</li>
<li>
<b>The loop short-circuits -- meaning not all items are processed.</b>
</li>
</ol>
<p>
Depending on what we are doing in the parallel loop, these items may not be a
concern. But there are situations where I would like to get all of the
exceptions back; and there are times when I would like to capture the
exceptions and continue processing.
</p>
<p>
Over the next several articles, we will look at these issues and how to deal
with them.
</p>
<p></p>
<ul style="text-align: left;">
<li>Parallel.ForEachAsync and Exceptions <i>this article</i></li>
<li><a href="https://jeremybytes.blogspot.com/2024/02/getting-multiple-exceptions-from.html">Getting Multiple Exceptions from Parallel.ForEachAsync</a></li>
<li><a href="https://jeremybytes.blogspot.com/2024/02/continue-processing-with.html">Continue Processing with Parallel.ForEachAsync (even when exceptions are thrown)</a></li>
</ul>
<p></p>
<p>
Code samples for all articles are available here: <a href="https://github.com/jeremybytes/foreachasync-exception">https://github.com/jeremybytes/foreachasync-exception</a>.
</p>
<p>
This article shows the basics of using Parallel.ForEachAsync and what happens
when exceptions are thrown.
</p>
<p>
<i>For slides and code samples on Parallel.ForEachAsync (and other parallel
approaches), you can take a look at the materials from my full-day workshop
on asynchronous programming: <a href="https://github.com/jeremybytes/async-workshop-2022">https://github.com/jeremybytes/async-workshop-2022</a>. (These materials use .NET 6.0. Updates for .NET 8.0 are coming in a few
months.) For announcements on public workshops, check here: <a href="https://jeremybytes.blogspot.com/p/workshops.html">https://jeremybytes.blogspot.com/p/workshops.html</a>.</i>
</p>
<h2 style="text-align: left;">Parallel.ForEachAsync Basics</h2>
<p><i>The code for today's article is in the <a href="https://github.com/jeremybytes/foreachasync-exception/tree/main/ForEachAsyncException/original-code">"original-code" folder</a> of the GitHub repository. The early code is work-in progress; only the repository only contains the finished code at the end of the article.</i></p><p>
We'll start by looking at the basics of Parallel.ForEachAsync. Here's a bit of
code that sets up a non-parallel loop:
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieMHeQYc_jODDYfqQN-ox9RJfKzbrff75yETXT6Xo8Mtro7VvPZIxsebqiDdjkMbaa7WdKnKBbmMzeuL5KOySLasFbUvI8FucOFECiTA_a6POSCmc5uG3LtXlkS68PNjyllkvAMzLvsI7oKym8vjBw_rocST3f8T0pUJPHHDbl9V14qvW3BdjrGb9NvL4/s856/01-foreach.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="397" data-original-width="856" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieMHeQYc_jODDYfqQN-ox9RJfKzbrff75yETXT6Xo8Mtro7VvPZIxsebqiDdjkMbaa7WdKnKBbmMzeuL5KOySLasFbUvI8FucOFECiTA_a6POSCmc5uG3LtXlkS68PNjyllkvAMzLvsI7oKym8vjBw_rocST3f8T0pUJPHHDbl9V14qvW3BdjrGb9NvL4/w640-h296/01-foreach.png" width="640" /></a>
</div>
<br />
<code>
<pre> static async Task Main(string[] args)
{
Console.Clear();
foreach (int i in Enumerable.Range(1, 100))
{
Console.WriteLine($"Processing item: {i}");
await Task.Delay(10); // simulate async task
}
Console.WriteLine("Done (Original Code)");
}</pre>
</code>
<p>
This code uses a regular foreach loop to iterate from 1 to 100. Inside the
loop, we output to the console and simulate some async work with
"Task.Delay(10)". This will delay processing for 10 milliseconds. Since this
code is running sequentially, it will take about 1 second for the entire loop
to complete.
</p>
<p>Here is what the output look like:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP-TiHZE3ApayIpvy-uurjfDVpFRvoiE7wy0cuyjnGirwP7T9J4tWsYq4_DPbSrv6CD9RqC6rzALyVf3QTjiGCh_lF1XK8aT9oFG41Tsx5lZEKnW0hq3mSJxsgMZcQ5KFgADrsX7kqmL4L13O34q5VNT_J-3esyPMYPG_plqutz453yAyLRXJvEBAUTcc/s426/foreach.gif" style="margin-left: 1em; margin-right: 1em;"><img alt="An animation of console output showing "Processing Item: 1" "Processing Item: 2" all the way to "Processing Item: 100". It takes about 1 second to complete the list. At the end "Done (Original Code)" is output." border="0" data-original-height="240" data-original-width="426" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP-TiHZE3ApayIpvy-uurjfDVpFRvoiE7wy0cuyjnGirwP7T9J4tWsYq4_DPbSrv6CD9RqC6rzALyVf3QTjiGCh_lF1XK8aT9oFG41Tsx5lZEKnW0hq3mSJxsgMZcQ5KFgADrsX7kqmL4L13O34q5VNT_J-3esyPMYPG_plqutz453yAyLRXJvEBAUTcc/w640-h360/foreach.gif" width="640" /></a>
</div>
<br />
<h3 style="text-align: left;">Using Parallel.ForEachAsync</h3>
<p>The next step is to change this to a parallel loop:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9ScVyR35IAYSS3qoqCOlrIBcAVrqNrKdNrKxkYhMPynQtH62opcocljMCC-_dQnTckLtMZxcNhcW14k_TW1xDthzcsEQ2PZu3BtTVzpxUkfx1cCLVraQA5vFd2Tme_rc2HvcZrBlPC_NnF7Le6hssvq1_PhevtUrmj0j7tijmlyOfGCTcS1rM-nPIg9I/s922/02-foreachasync.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="475" data-original-width="922" height="330" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9ScVyR35IAYSS3qoqCOlrIBcAVrqNrKdNrKxkYhMPynQtH62opcocljMCC-_dQnTckLtMZxcNhcW14k_TW1xDthzcsEQ2PZu3BtTVzpxUkfx1cCLVraQA5vFd2Tme_rc2HvcZrBlPC_NnF7Le6hssvq1_PhevtUrmj0j7tijmlyOfGCTcS1rM-nPIg9I/w640-h330/02-foreachasync.png" width="640" /></a>
</div>
<br />
<code>
<pre> static async Task Main(string[] args)
{
Console.Clear();
await Parallel.ForEachAsync(
Enumerable.Range(1, 100),
async (i, _) =>
{
Console.WriteLine($"Processing item: {i}");
await Task.Delay(10); // simulate async task
});
Console.WriteLine("Done (Original Code)");
}</pre>
</code>
<p>Here are a couple of notes on how this code works:</p>
<p>
First, notice that we "await" the Parallel.ForEachAsync method. The loop runs
asynchronously, so if we do not "await" here, then the Main method would keep
going. Because of the "await", the last line (writing "Done" to the console)
will not run until after all iterations of the loop are complete.
</p>
<p>Next, let's look at the parameters for "ForEachAsync".</p>
<p>
The first parameter (<code>Enumerable.Range(1, 100)</code>) is the IEnumerable
to iterate through. This is the same as the "in" part of the non-parallel
foreach loop.
</p>
<p>
The second parameter is a delegate that has the work we want to run in
parallel.
</p>
<p>
<b>Delegate Parameter</b><br />This delegate has 2 parameters (which we have
as <code>(i, _)</code> here). The "i" parameter is the item in the current
iteration of the loop. This is equivalent to the "i" in the foreach loop. We
can use "i" inside the delegate body just like we can use "i" inside the body
of the foreach loop.
</p>
<p>
The second parameter of the delegate is a CancellationToken. Since we are not
dealing with cancellation here, we use a discard "_" to represent this
parameter.
</p>
<p>
The body of the delegate has the actual work. This is the same as the contents
of the foreach loop above. We output a line to the console and then simulate
some work with <code>await Task.Delay(10)</code>.
</p>
<p>
Because we have "await" in the body of the delegate, the delegate itself is
also marked with the "async" modifier (before the parameters).
</p>
<p>
<b>Output</b><br />Because our code is now running in parallel, it completes
much faster. Here is what the output looks like (it is too fast to see well):
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHZBmkF21Er_-5jpEI1l7v4Hnu8voDAqkPhNBt9D48T9IO2PM_Msf23Ij9U6hSg11a_fQp-77hT6srTTj8fkG1UjojY81BVJOHbumJ7_GwT7133sXggwX5PeXoJ3Km-IrcWiNc5o7fb4mhTcbKUV45UspqXXvQ7_BJZK0i2y1VpTu8Ebh6dK8GawYMIiI/s426/foreachasync.gif" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="240" data-original-width="426" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHZBmkF21Er_-5jpEI1l7v4Hnu8voDAqkPhNBt9D48T9IO2PM_Msf23Ij9U6hSg11a_fQp-77hT6srTTj8fkG1UjojY81BVJOHbumJ7_GwT7133sXggwX5PeXoJ3Km-IrcWiNc5o7fb4mhTcbKUV45UspqXXvQ7_BJZK0i2y1VpTu8Ebh6dK8GawYMIiI/w640-h360/foreachasync.gif" width="640" /></a>
</div>
<br />
<p>
The speed will depend on how many virtual cores are available to do the
processing. Parallel.ForEachAsync normally figures out how many resources to
use on its own. We'll add some hints to it later on so we can get more
consistent results.
</p>
<p>
One thing to note about the output is that "100" prints out before "98". This
is one of the joys of parallel programming -- order is non-deterministic.
</p>
<p>
Now let's move on to see what happens when one or more of these items throws
an exception.
</p>
<h2 style="text-align: left;">Throwing an Exception</h2>
<p>Here's a method that sometimes throws an exception:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQEjvIP6BpDUsum05e0tE849NWkbdAlH9KFiRYrneTYmWgStBzRfwFLDmkoohKVr8QkBslKX8E1CY7vDtfsvrWajfSl-UkRwTwulo0Kk_foll3iRbhZMEbY0lTtvjFhClM4Pv0-VcPtPr0jRgZELVZmB-LsYZD9PuABdiW4Ho6gfXYzDZWgllC0-wWl30/s1170/03-mightthrow.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="267" data-original-width="1170" height="146" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQEjvIP6BpDUsum05e0tE849NWkbdAlH9KFiRYrneTYmWgStBzRfwFLDmkoohKVr8QkBslKX8E1CY7vDtfsvrWajfSl-UkRwTwulo0Kk_foll3iRbhZMEbY0lTtvjFhClM4Pv0-VcPtPr0jRgZELVZmB-LsYZD9PuABdiW4Ho6gfXYzDZWgllC0-wWl30/w640-h146/03-mightthrow.png" width="640" /></a>
</div>
<br />
<code>
<pre> private static void MightThrowException(int item)
{
if (item % 3 == 0)
{
Interlocked.Increment(ref TotalExceptions);
throw new Exception($"Bad thing happened inside loop ({item})");
}
}</pre>
</code>
<p>
This will throw an exception for every 3rd item. (We could hook this up to a
random number generator, but this gives us some predictability while we look
at results.)
</p>
<p>
<b>Interlocked.Increment</b><br />There may be a line of code that does not
look familiar here:
</p>
<p><code> Interlocked.Increment(ref TotalExceptions);</code></p>
<p>
In this case "TotalExceptions" is a static integer field at the top of our class. This
lets us keep track of how many exceptions are thrown.
</p>
<p>
"Interlocked.Increment" is a thread-safe way to increment a shared integer.
Using the "++" operator is not thread-safe, and may result in incorrect
values.
</p>
<p>
<b>Exceptions in the ForEachAsync Loop</b><br />Now we'll update the code to
call "MightThrowException" inside our loop. Since we do not want an unhandled
exception here, we will wrap the whole thing in a try/catch block:
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNglbf9Qu51yjM-Mjn9PnPCotQ_s6VMUf9d9MGZrMb-I2B1elqB4VmoPFFpjtfxMf2tww9F_zQbDXDLwgjo7aTGKThiG7dojJ4nFg99OlYBeaV2V_jSJk48_ptN3YwGrSOs8LTkFAoN3fNCLXyydT7m9AYbim-LABUSuGaSKJhkpzuFX8qb-oOSiSIv_c/s1017/04-final-foreachasync.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="823" data-original-width="1017" height="518" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgNglbf9Qu51yjM-Mjn9PnPCotQ_s6VMUf9d9MGZrMb-I2B1elqB4VmoPFFpjtfxMf2tww9F_zQbDXDLwgjo7aTGKThiG7dojJ4nFg99OlYBeaV2V_jSJk48_ptN3YwGrSOs8LTkFAoN3fNCLXyydT7m9AYbim-LABUSuGaSKJhkpzuFX8qb-oOSiSIv_c/w640-h518/04-final-foreachasync.png" width="640" /></a>
</div>
<br />
<code>
<pre> static async Task Main(string[] args)
{
Console.Clear();
try
{
await Parallel.ForEachAsync(
Enumerable.Range(1, 100),
async (i, _) =>
{
Console.WriteLine($"Processing item: {i}");
await Task.Delay(10); // simulate async task
MightThrowException(i);
Interlocked.Increment(ref TotalProcessed);
});
}
catch (Exception ex)
{
Console.WriteLine($"Exception: {ex.Message}");
}
Console.WriteLine($"\nTotal Processed: {TotalProcessed}");
Console.WriteLine($"Total Exceptions: {TotalExceptions}");
Console.WriteLine("Done (Original Code)");
}</pre>
</code>
<p>We've changed quite a few things.</p>
<p>
First, we have wrapped the entire "ForEachAsync" call in a try/catch block.
This is to make sure we do not have an unhandled exception.
</p>
<p>
Next, we have added the "MightThrowException" call inside of our loop. This
will throw an exception for every 3rd item.
</p>
<p>
Next, we added "<code>Interlocked.Increment(ref TotalProcessed);</code>". This
is after the point an exception might be thrown. So if an exception is not
thrown, we increment a "TotalProcessed" field (similar to the
"TotalExceptions" field). This will give us a count of the items that were
processed successfully.
</p>
<p>In the "catch" block, we output the exception message.</p>
<p>
Finally, we have console output for the total number of items processed
successfully and the total number of exceptions.
</p>
<p>
<b>Output</b><br />Here is the output for this code (note: this output is not
animated):
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcLdkOXkZGMtUYvN1J7YK7BA8j2PC3UV5AwyN21gsCzNMSdtPHvX8BK1lREy19KJbewrwPNPiq6ceQpbtAQK6yayG42_auv6B4bi6tX-u4e4nw_ii8cxFpAfhM2FZ8m5saB0LRIypH0J00sooQx3EvnFV3hQWok6J_MNbt5LMw-WmzNCwhsbJzzmNzhYc/s917/05-output1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="721" data-original-width="917" height="504" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcLdkOXkZGMtUYvN1J7YK7BA8j2PC3UV5AwyN21gsCzNMSdtPHvX8BK1lREy19KJbewrwPNPiq6ceQpbtAQK6yayG42_auv6B4bi6tX-u4e4nw_ii8cxFpAfhM2FZ8m5saB0LRIypH0J00sooQx3EvnFV3hQWok6J_MNbt5LMw-WmzNCwhsbJzzmNzhYc/w640-h504/05-output1.png" width="640" /></a>
</div>
<br />
<code>
<pre>Processing item: 15
Processing item: 16
Processing item: 17
Processing item: 26
Processing item: 18
Processing item: 19
Processing item: 20
Processing item: 21
Processing item: 22
Processing item: 23
Processing item: 24
Processing item: 25
Processing item: 27
Exception: Bad thing happened inside loop (6)
Total Processed: 18
Total Exceptions: 9
Done (Original Code)</pre>
</code>
<p>
This is just the last part of the output, but it tells us enough of about what
is happening.
</p>
<h2 style="text-align: left;">The Issues</h2>
<p>
The output shows us the 2 issues from the start of this article. These may or
may not concern us, depending on what we are doing. But we do need to be aware
of them.<br />
</p>
<p><b>Hidden Exceptions</b></p>
<blockquote style="border: medium; margin: 0px 0px 0px 40px; padding: 0px;">
<p style="text-align: left;">
1.
<b>If we "await" ForEachAsync, then we get a single exception (even if
exceptions are thrown in multiple iterations of the loop).
</b>
</p>
</blockquote>
<p></p>
<p>
When an exception is thrown in a Task, it gets wrapped in an
AggregateException. This is because Tasks can be complex (with concurrent and
child tasks). An AggregateException wraps up all of the exceptions that happen
into a single exception.
</p>
<p>
<b>But</b> when we "await" a Task, the AggregateException gets unwrapped
for us. This can be good because we now have a "real" exception and do not
have to deal with an AggregateException. But it can be bad because it hides
the number of exceptions that actually occur.
</p>
<p>
Since we "await" the ForEachAsync method, we only see one exception:
"Exception: Bad thing happened inside loop (6)". So this is only showing the
exception for item #6.
</p>
<p>
But we can see in the "Total Exceptions" that 9 exceptions were thrown. The
other exceptions are hidden from us here.
</p>
<p><b>Short-Circuited Loop</b></p>
<blockquote style="border: medium; margin: 0px 0px 0px 40px; padding: 0px;">
<p style="text-align: left;">
2. <b>The loop short-circuits -- meaning not all items are processed. </b>
</p>
</blockquote>
<p>
The other thing to notice about the output is that the loop stops processing
part way through. Only 27 of the 100 iterations of the loop ran. This is the
nature of the ForEachAsync method. If a task throws an exception, the loop
stops processing.
</p>
<p>
Depending on our scenario, we may want the loop to continue even if one of the
iterations throws an exception.
</p>
<p>We deal with both of these items in the next 2 articles.</p>
<h2 style="text-align: left;">A Little Consistency</h2>
<p>Before leaving this code, let's add a little bit of consistency.</p>
<p>
One of the problems with parallel code is that the decision of how many items
to run at the same time is left up to the parallel infrastructure. If we have
a lot of resources available, then there will be more items run in parallel.
</p>
<p>
But this also means that output will vary depending on what machine we are
running on (and how many resources that machine has at the time). In this
code, my desktop and laptop produce different results. The desktop generally
stops after 27 items, the laptop will stop after 17 (sometimes fewer,
depending on what else is going on).
</p>
<p>
Parallel.ForEachAsync has an optional parameter where we can set the maximum
degrees of parallelism. This will limit the number of items run concurrently,
and if we set this to a value lower than our machine resources, will also add
some consistency to the output.
</p>
<p>
Here is our loop with the additional parameter. (This is the final state of
our "original-code" project and can be found in the
<a href="https://github.com/jeremybytes/foreachasync-exception/blob/main/ForEachAsyncException/original-code/Program.cs">original-code/Program.cs</a>
file.)
</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXxjAq5W0ucEafe8pM-IZH72-BOnSUZZpUC4IyPZrFdQBQm8E7bi7w03qWAD03lbvLnbjPOCtFfGrCjyNDfOncv1o1Tgs06GbuZWnGmp-eiIuTRI22dVCefHP1HNFtaf1i4aKwM0OWK_F27GHW4LoaDAPhQ1u6ivGmsnXRPOv3bLfeHXmpxEQM8gm_NAI/s942/05-paralleloptions.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="357" data-original-width="942" height="242" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXxjAq5W0ucEafe8pM-IZH72-BOnSUZZpUC4IyPZrFdQBQm8E7bi7w03qWAD03lbvLnbjPOCtFfGrCjyNDfOncv1o1Tgs06GbuZWnGmp-eiIuTRI22dVCefHP1HNFtaf1i4aKwM0OWK_F27GHW4LoaDAPhQ1u6ivGmsnXRPOv3bLfeHXmpxEQM8gm_NAI/w640-h242/05-paralleloptions.png" width="640" /></a>
</div>
<br />
<code>
<pre> await Parallel.ForEachAsync(
Enumerable.Range(1, 100),
new ParallelOptions() { MaxDegreeOfParallelism = 10 },
async (i, _) =>
{
Console.WriteLine($"Processing item: {i}");
await Task.Delay(10); // simulate async task
MightThrowException(i);
Interlocked.Increment(ref TotalProcessed);
});</pre>
</code>
<p>
This second parameter is a ParallelOptions object that sets the
MaxDegreesOfParallelism property to 10. This means that a maximum of 10 items
run concurrently. (It may be fewer items if there are not enough resources
available.)
</p>
<p>This gives me a consistency between my mid-range machines.</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeELDmjEDGYYe-RnQyrByem44CeRcrPh3-bbVSlUlglFH7UVubh4LdrsP4y6fM7z7m3eiCudL5r9MKwZROEtewWRYqJb-f5xQ1Zw7-eB-g4vruX8m0Kk_ew62ueUjiQQkrcIohd0ab1PNizAhQDZ0J0IKgD2D_c7W-pjQ8R6jYG-fJRdG1Zo0HbTTb9tE/s913/07-output2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="723" data-original-width="913" height="506" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeELDmjEDGYYe-RnQyrByem44CeRcrPh3-bbVSlUlglFH7UVubh4LdrsP4y6fM7z7m3eiCudL5r9MKwZROEtewWRYqJb-f5xQ1Zw7-eB-g4vruX8m0Kk_ew62ueUjiQQkrcIohd0ab1PNizAhQDZ0J0IKgD2D_c7W-pjQ8R6jYG-fJRdG1Zo0HbTTb9tE/w640-h506/07-output2.png" width="640" /></a>
</div>
<br />
<code>
<pre>Processing item: 6
Processing item: 9
Processing item: 5
Processing item: 1
Processing item: 3
Processing item: 8
Processing item: 11
Processing item: 16
Processing item: 12
Processing item: 13
Processing item: 14
Processing item: 15
Processing item: 17
Exception: Bad thing happened inside loop (9)
Total Processed: 12
Total Exceptions: 5
Done (Original Code)</pre>
</code>
<p>
Now I get a fairly consistent 17 items processed. I want the consistency here
so that we can more readily compare results when we look at different ways of
handling issues.
</p>
<h2 style="text-align: left;">Wrap Up</h2>
<p>
So to recap, here are 2 things that we need to be aware of when we use
Parallel.ForEachAsync:
</p>
<ol style="text-align: left;">
<li>
<b>
If we "await" ForEachAsync, then we get a single exception (even if
exceptions are thrown in multiple iterations of the loop).
</b>
</li>
<li>
<b>The loop short-circuits -- meaning not all items are processed.</b>
</li>
</ol>
<p></p>
<p>
This may be fine for the work that we are doing, but we may want to go beyond
that. We will tackle the first item in the next article. We'll look at 2
different ways to get all of the exceptions from ForEachAsync, and why we
might choose one rather than the other.
</p>
<p>
In the 3rd article, we will tackle the issue of short-circuiting. So be sure
to check back.
</p>
<p>Happy Coding!</p>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-24481846101517177352024-02-23T11:53:00.000-08:002024-02-24T07:31:56.396-08:00Have you lived more than 5 days in the last 4 months?<p>Last night (more correctly, very early this morning), I learned something new about myself:</p><blockquote><p><span style="font-size: large;">I haven't been living every day.</span></p></blockquote><p>I read 1970s science fiction. I have over 600 physical books
in my collection, and I've read about half so far. My collection started about 7 years ago (I'm still not sure why I ended up with this category). Here's a recent picture of my collection:
</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmftrCC-l5UTseVBLkwvMWBYkYdEIhkBwb6DxjwRu01qsSwRPdOTJoYYHdLTiA8O2YsY3BCB7UvhEBXOT7erG0iD2Uysc7vM6WU2ZudDwhbQbINEruG0QHbvBrrkGziiKg_XwiJvDMVVKqaYrhvJFwGG4YJlf1t4DLzTJprfkmyhnnE76sboHU0sYJ2sg/s4080/PXL_20231028_171009153~2.jpg" style="margin-left: 1em; margin-right: 1em;"><img alt="2 bookshelves full of mostly paperback books." border="0" data-original-height="4080" data-original-width="3072" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmftrCC-l5UTseVBLkwvMWBYkYdEIhkBwb6DxjwRu01qsSwRPdOTJoYYHdLTiA8O2YsY3BCB7UvhEBXOT7erG0iD2Uysc7vM6WU2ZudDwhbQbINEruG0QHbvBrrkGziiKg_XwiJvDMVVKqaYrhvJFwGG4YJlf1t4DLzTJprfkmyhnnE76sboHU0sYJ2sg/w482-h640/PXL_20231028_171009153~2.jpg" width="482" /></a></div>
<p>
Some if it is good; some is bad; some is amazing. They are rarely life-changing, but I was floored by a very
timely passage from an otherwise mediocre book:
<i>The Earth Tripper</i> by Leo P. Kelley.
</p>
<h2 style="text-align: left;">The Earth Tripper</h2>
<p></p>
<blockquote>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwTbRHEET4M2aqTxQGVJ2-e4Ww2CZI1iqNjK8EkCY5orV0K0_g9myvksZG1r5Zfcy0QhDDCjY2OYM56lItBHyrlrpFNrC0YD9iafHC6dSh4qs-WryKBG0zn9ARio8jPLue3HKWd0F5WPiVIp2bG53214h3BgNrs3MQI7Ku0dzvd-QlKExz-o_2If7gnlo/s3269/PXL_20240223_170730772.jpg" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="Book Cover: The Earth Tripper by Leo P. Kelley featuring a helicopter above a dome." border="0" data-original-height="3269" data-original-width="2009" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwTbRHEET4M2aqTxQGVJ2-e4Ww2CZI1iqNjK8EkCY5orV0K0_g9myvksZG1r5Zfcy0QhDDCjY2OYM56lItBHyrlrpFNrC0YD9iafHC6dSh4qs-WryKBG0zn9ARio8jPLue3HKWd0F5WPiVIp2bG53214h3BgNrs3MQI7Ku0dzvd-QlKExz-o_2If7gnlo/w197-h320/PXL_20240223_170730772.jpg" width="197" /></a>
</div>
"When I was a child, I never lived in the present. I was always waiting
for--oh, for Saturday to come, because on Saturday I had been promised a new
bicycle. So Tuesday and all the other days didn't matter at all while I waited
for Saturday to come. It came. So did the new bicycle and I was happy. But
then it was Sunday all of a sudden and the bicycle wasn't quite so new
anymore. I heard that the circus was coming to our town. I watched the men put
up the posters all over town, and I got my sister--she was three years older
than I was--to read the date to me--you know, the date the circus would
arrive. I went home and made a red circle around the day on our kitchen
calendar. I waited and waited, and at last the circus came. For a time, the
lions and tigers, the bareback riders, and all the handsome aerialists filled
my world. But next day, they were gone.
<p></p>
<p>
"One day I took the calendar down from the kitchen wall and I counted all
the days that I had circled in red. There were five of them from January to
May. Then I counted all the ordinary days and I realized that I had been
cheating myself. My mother made me stay in my room alone that night while
the family ate dinner without me. She couldn't understand why I had torn the
calendar to shreds."
</p>
<p>"Didn't you explain to her what you had discovered?"</p>
<p>
"I didn't know how to explain. I was ashamed.
<b>How could I tell anyone--how could I dare tell anyone that I had really
lived only five days in four months?</b>"
</p>
</blockquote>
<p>[pp 139-140, <i>The Earth Tripper</i>, Leo P. Kelley, Coronet Books, 1974, Emphasis mine]</p>
<p>
This was particularly impactful to me because I wasn't expecting it. It was an
otherwise mediocre book. I only had about 20 pages left. And I was reading it
at 1:30 in the morning (because I couldn't sleep). I was just trying to finish
up this book.
</p>
<p>
But when I got to this passage, I realized that I have been living for the red
circles.
</p><p>And how could I dare tell anyone that I had really lived only five days in four months?</p>
<h2 style="text-align: left;">Red Circles</h2>
<p>
For me, the red circle days are the days when I get to help other developers,
when I get to make a difficult topic understandable, when I get to show
someone something they didn't know. Historically I have done this is a number
of ways: blog articles, videos, online courses, speaking engagements,
workshops, and one-on-one mentoring.
</p>
<p>
But I have been limiting my red circle days even further by only including
the days I help someone actively rather than passively.
</p>
<h3 style="text-align: left;">Active vs. Passive</h3>
<p><b>
Active help</b> is when I get feedback while I am doing it. An example of
this is giving a conference talk. While giving the talk, I can watch the
lightbulbs go on -- I can see when someone is really understanding the topic and
is excited to make use of what they have learned. It is also the conversations
that happen after the talk: helping folks with their specific questions,
clarifying a point, or going deeper into a specific area that the talk does
not allow for.
</p>
<p>
These are the times that I feel most useful. And these are the times that I
know that my particular skillset allows me to make complex topics more
approachable. And these are the times when I know that I am exactly where I
need to be.
</p>
<p>
I know that these active events do not reach everyone in the room. About 10%
get enough out of it to leave a glowing rating or a comment. About 5% find it so not useful to them that they leave a comment. And I assume everyone else gets something
out of it (probably not life-changing, but maybe useful some day) -- at
minimum, they didn't hate it. But I am able to help some people (and see it), and that is enough for me to keep going.</p>
<p><b>
Passive help</b> is when I do not get to see the impact of my work. For example,
on this blog. I have written over 500 articles (over 15 years). Some of these
are more useful than others. I don't get a huge amount of traffic, but I do
get about 20,000 - 30,000 views per month. If I make an assumption that 1% of
those views are actually useful to someone, that means that I help 6 - 10
people a day.
</p>
<p>But I don't usually think about these 6 - 10 people because I don't get to actually see
them.
</p><p>So my red circles show up on the active help days but not the passive help days.</p>
<h2 style="text-align: left;">Too Few Circles</h2>
<p>
I'm not sure when I started relying on red circle days. For most of my
speaking career (career isn't the right word, but I'll use it here), I have
averaged about 1 event every 3 weeks. And during my peak year, it was a lot
more frequent than that.
</p>
<p>
But things change. When I moved out of Southern California, I lost access to
the local user groups, community events, and local-ish events (within a
half-day drive). With COVID, most events did not happen (some went online).
And in the past couple of years, several of my favorite events have gone away.
</p>
<p>
This year, I am confirmed for 2 events so far. I have another 3 or 4 potentials. The hardest part is that there were several events that I was
relying on that did not select me. I have been having quite a bit of trouble with those (for a variety of reasons).</p>
<p>
The circles are very far apart. Right now, I am in a spot with 6 months
between circles.
</p>
<h2 style="text-align: left;">Finding Life on the In-Between Days</h2>
<p>Recently, I have really been feeling a big gap in my life -- like I am merely passing time until the next big thing. </p><p>The passage from the book made me realize that I have been living for the red circle days. And I have been getting away with it for a long time because the circles were fairly close together. Somewhere along the
way, I forgot about all of the days in between.</p>
<p><span style="font-size: large;"></span></p>
<blockquote>
<span style="font-size: large;">The point is not to figure out how to get more red circles. The point is to
figure out how to find life on all of the other days.</span>
</blockquote>
<p></p>
<p>Honestly, I'm not quite sure how I am going to do this yet. But that's okay.</p><h2 style="text-align: left;">Rough Days</h2><p>The last couple months have been kind of rough for me -- and not because of the red circle days. I have discovered some things about myself that have impacted how I look at the world, how I see myself, and how I interact with other people. These discoveries are difficult to work through but are ultimately good.</p><p>I have been able to put names and ideas on things that I have recognized in myself and my life. It has been hard because a lot of things I thought I knew about myself had to be reframed (and a bit of that reframing is still happening).</p><p>And now I am aware of the impact of red circle days. I knew that there was something wrong, but I didn't know what. Now that I have identified an issue, I can go about changing things.</p><p></p><blockquote><span style="font-size: large;">Are you living just for red circle days? If so, I challenge you to find the life on the days in between.</span></blockquote><p></p><p>I am going to find that life, and I am going to start living it.</p><p>Happy Living!</p>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-89882698190999599202024-02-22T06:30:00.000-08:002024-02-22T06:55:03.290-08:00Minimal APIs vs Controller APIs: SerializerOptions.WriteIndented = true<div>Another thing added in ASP.NET Core / .NET 7.0 (yeah, I know it's been a
while since it was released), is the "ConfigureHttpJsonOptions" extension
method that lets us add JSON options to the services collection / dependency
injection container. The setting that caught my eye is "WriteIndented = true".
This gives a nice pretty output for JSON coming back from our APIs.
</div>
<div><br /></div>
<div>This is the difference between:</div>
<div><br /></div>
<code>{"id":3,"givenName":"Leela","familyName":"Turanga","startDate":"1999-03-28T00:00:00-08:00","rating":8,"formatString":"{1}
{0}"}</code>
<div><br /></div>
<div>and</div>
<code>
<pre>{
"id": 3,
"givenName": "Leela",
"familyName": "Turanga",
"startDate": "1999-03-28T00:00:00-08:00",
"rating": 8,
"formatString": "{1} {0}"
}</pre>
</code>
<div>
You may not want this because of the extra whitespace characters that have to
come down the wire. But for the things that I work with, I want the pretty
printing!
</div>
<div><br /></div>
<div>
The good news is that in .NET 7.0, the new "ConfigureHttpJsonOptions" method
lets us set this up (among quite a few other settings).
</div>
<div><br /></div>
<div>
To use it, we just add the options to the Services in the Program.cs file of
our project.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPbRFnldPVw5Tq1GU6R_UKc8eePhiRDddXTVa-1KzRGwOnNU-JRSTSBl7mJfsoACdzgwaWbeuolIMk6VOs2mrrkPPVmOvbf6bU6UR2u4sUAmKd2bGVSMQLsFGpeG1pFZtL4hAGdZIKsncAMRT8R1n8VRGNTXNCduHpBY-E-mQUiIqLDK_L_in5hXIDxLA/s1270/01-setting-options.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="148" data-original-width="1270" height="74" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPbRFnldPVw5Tq1GU6R_UKc8eePhiRDddXTVa-1KzRGwOnNU-JRSTSBl7mJfsoACdzgwaWbeuolIMk6VOs2mrrkPPVmOvbf6bU6UR2u4sUAmKd2bGVSMQLsFGpeG1pFZtL4hAGdZIKsncAMRT8R1n8VRGNTXNCduHpBY-E-mQUiIqLDK_L_in5hXIDxLA/w640-h74/01-setting-options.png" width="640" /></a>
</div>
<br />
<code>
<pre> // Set JSON indentation
builder.Services.ConfigureHttpJsonOptions(
options => options.SerializerOptions.WriteIndented = true);
</pre>
</code>
<div><br /></div>
<div>
You can check the documentation for
<a href="https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializeroptions?view=net-8.0">JsonSerializerOptions Class</a>
to see what other options are available.
</div>
<div><br /></div>
<div>But there's a catch:</div>
<blockquote>
<span style="font-size: large;">ConfigureHttpJsonOptions <b>does</b> work for Minimal APIs.
<br />ConfigureHttpJsonOptions <b>does not</b> work for Controller
APIs. </span>
</blockquote>
<div>
Let's take a look at that. For code samples, you can check out this
repository:
<a href="https://github.com/jeremybytes/controller-vs-minimal-apis">https://github.com/jeremybytes/controller-vs-minimal-apis</a>.
</div>
<div><br /></div>
<h2 style="text-align: left;">Sample Application</h2>
<div>
The sample code contains a "<a href="https://github.com/jeremybytes/controller-vs-minimal-apis/tree/main/DemoCode/MinimalApi">MinimalApi</a>" project and a "<a href="https://github.com/jeremybytes/controller-vs-minimal-apis/tree/main/DemoCode/ControllerApi">ControllerApi</a>" project. These were both started as fresh projects using the "dotnet new
webapi" template (and the appropriate flags for minimal/controller). Both
projects get their data from a 3rd project ("<a href="https://github.com/jeremybytes/controller-vs-minimal-apis/tree/main/DemoCode/People.Library">People.Library</a>") that provides some hard-coded data.
</div>
<div><br /></div>
<div>Here is the minimal API that provides the data above:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaKquxl-ht3cWhwPUWLEOFwDMfxqGsJ4Fcl4MgAgqBVvC34Yushq03-ZfveXNH1wgpUeJY2a6IuDZ7rZJlgr87Vs6tVP4U30REYqYDP20gHRAFO4zHK0M6dNwlGNfq9O31Y3c7_wJ5N66znwEtbvWXKG5DQUXh42Wt-44MucM4p9dPQ6rtFrmO9qzDfgs/s1564/02-minimal-api.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="184" data-original-width="1564" height="76" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgaKquxl-ht3cWhwPUWLEOFwDMfxqGsJ4Fcl4MgAgqBVvC34Yushq03-ZfveXNH1wgpUeJY2a6IuDZ7rZJlgr87Vs6tVP4U30REYqYDP20gHRAFO4zHK0M6dNwlGNfq9O31Y3c7_wJ5N66znwEtbvWXKG5DQUXh42Wt-44MucM4p9dPQ6rtFrmO9qzDfgs/w640-h76/02-minimal-api.png" width="640" /></a>
</div>
<br />
<code>
<pre> app.MapGet("/people/{id}",
async (int id, IPeopleProvider provider) => await provider.GetPerson(id))
.WithName("GetPerson")
.WithOpenApi();</pre>
</code>
<div><br /></div>
<div>And here's the controller API:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgepMMldm8E5xQ4jFBB8GS_rHsFqGCx1Is569etVetNCTTXk_h2gKz-Z4dj01ZeKXxeK5zl1W5j_tHJ8HGd_-ri58gHpLXLqPaGljWD70lBEaDoTJ7KK54X7KB9S5DuX8oFnk3C9Rh_VL3gR1UBREQStZgWiTIplDntzXenWcqajMowl-Q8J2-DmTjJ-WM/s1066/03-controller-api.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="325" data-original-width="1066" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgepMMldm8E5xQ4jFBB8GS_rHsFqGCx1Is569etVetNCTTXk_h2gKz-Z4dj01ZeKXxeK5zl1W5j_tHJ8HGd_-ri58gHpLXLqPaGljWD70lBEaDoTJ7KK54X7KB9S5DuX8oFnk3C9Rh_VL3gR1UBREQStZgWiTIplDntzXenWcqajMowl-Q8J2-DmTjJ-WM/w640-h196/03-controller-api.png" width="640" /></a>
</div>
<br />
<code>
<pre> [HttpGet("{id}", Name = "GetPerson")]
public async Task<Person?> GetPerson(
[FromServices] IPeopleProvider provider, int id)
{
// This may return null
return await provider.GetPerson(id);
}</pre>
</code>
<div><br /></div>
<div>
Both of these call the "GetPerson" method on the provider and pass in the
appropriate ID.
</div>
<div><br /></div>
<div><b>Testing the Output</b></div>
<div>
To test the output, I created a console application (the
<a href="https://github.com/jeremybytes/controller-vs-minimal-apis/tree/main/DemoCode/ServiceTester">"ServiceTester" project</a>). I did this to make sure I was not getting any automatic JSON formatting
from a browser or other tool.
</div>
<div><br /></div>
<div>
I created an extension method on the Uri type to make calling easier. You can
find the complete code in the
<a href="https://github.com/jeremybytes/controller-vs-minimal-apis/blob/main/DemoCode/ServiceTester/RawResponseString.cs">RawResponseString.cs file</a>.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnoAz05G5mYQ5l9MQlA-55dQf4CWZREtiiVhwVuE61oAoo07aTUrgRvL91TSrLM5kGkJbJsM8x807hK9qSjLQMbz-prju7zQPIOp4bAe3rBF2iepEQzVaBHara2R6pGoMVZVffO5BL5WpIUWsC2YDGmA1VnWkjeimjgH03bCbVGs5bFaFWzNlFjBznZSI/s934/04-GetResponse.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="106" data-original-width="934" height="72" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnoAz05G5mYQ5l9MQlA-55dQf4CWZREtiiVhwVuE61oAoo07aTUrgRvL91TSrLM5kGkJbJsM8x807hK9qSjLQMbz-prju7zQPIOp4bAe3rBF2iepEQzVaBHara2R6pGoMVZVffO5BL5WpIUWsC2YDGmA1VnWkjeimjgH03bCbVGs5bFaFWzNlFjBznZSI/w640-h72/04-GetResponse.png" width="640" /></a>
</div>
<br />
<code>
<pre> public static async Task<string> GetResponse(
this Uri uri, string endpoint)</pre>
</code>
<div><br /></div>
<div>
The insides of this method make an HttpClient call and return the response.
</div>
<div><br /></div>
<div>
The calling code is in the
<a href="https://github.com/jeremybytes/controller-vs-minimal-apis/blob/main/DemoCode/ServiceTester/Program.cs">Program.cs file</a>
of the ServiceTester project.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjriPP4tp_YaXnJLcZOhzBraA5ISF6kORUxby_a3JC_ATSPcDuoO-_Zl-sYxHYVUCH-jgdvtv87j6YMt08lauqTlhYwCAO6nHC71uU_D-UP2tZDQYei1KH53UM1Rk0EQJl1kr6jGkQMfd4wVp5E3s8IkyMzceEpjjZrhuHzVZLqozNmdgnJYy910waPnCM/s1171/05-tester-app.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="718" data-original-width="1171" height="392" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjriPP4tp_YaXnJLcZOhzBraA5ISF6kORUxby_a3JC_ATSPcDuoO-_Zl-sYxHYVUCH-jgdvtv87j6YMt08lauqTlhYwCAO6nHC71uU_D-UP2tZDQYei1KH53UM1Rk0EQJl1kr6jGkQMfd4wVp5E3s8IkyMzceEpjjZrhuHzVZLqozNmdgnJYy910waPnCM/w640-h392/05-tester-app.png" width="640" /></a>
</div>
<br />
<code>
<pre> Uri controllerUri = new("http://localhost:5062");
Uri minimalUri = new("http://localhost:5194");
// Minimal API Call
Console.WriteLine("Minimal /people/3");
var response = await minimalUri.GetResponse("/people/3");
Console.WriteLine(response);
Console.WriteLine("----------");
// Controller API Call
Console.WriteLine("Controller /people/3");
response = await controllerUri.GetResponse("/people/3");
Console.WriteLine(response);
Console.WriteLine("----------");</pre>
</code>
<div><br /></div>
<div>
This creates 2 URIs (one for each API), calls "GetResponse" for each and then
outputs the string to the console.
</div>
<div><br /></div>
<div>
<i>This application was also meant to show some other differences between
Minimal APIs and Controller APIs. These will show up in future articles.</i>
</div>
<div><br /></div>
<h2 style="text-align: left;">Without "WriteIndented = true"</h2>
<div>
Here is the output of the ServiceTester application without making any changes
to the settings of the API projects.
</div>
<div><br /></div>
<div>
<i>Note that both the MinimalApi and ControllerApi services need to be running
in order for the ServiceTester application to work. (I start the services in
separate terminal tabs just so I can keep them running while I do my various
tests.)</i>
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_jbpaO6Jbl1g9PaNGVVhPEmagIKE7SB_WPVqElbVQeAodOc_H1VSxmLETXpKN1VGjmjqPI46jHnms3Ppd2zoX-Cy2X05cg_20cLbmekkb-9kEp2gHE628X3e9or6eUDCzSMEMGEI1mAgoRMT00VMBRrX05EgQ6pnEabwfR49eLePeqOs_I1r8aEdyDvk/s1564/06-initial-output.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="520" data-original-width="1564" height="212" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_jbpaO6Jbl1g9PaNGVVhPEmagIKE7SB_WPVqElbVQeAodOc_H1VSxmLETXpKN1VGjmjqPI46jHnms3Ppd2zoX-Cy2X05cg_20cLbmekkb-9kEp2gHE628X3e9or6eUDCzSMEMGEI1mAgoRMT00VMBRrX05EgQ6pnEabwfR49eLePeqOs_I1r8aEdyDvk/w640-h212/06-initial-output.png" width="640" /></a>
</div>
<br />
<code>Minimal /people/3<br />
{"id":3,"givenName":"Leela","familyName":"Turanga","startDate":"1999-03-28T00:00:00-08:00","rating":8,"formatString":"{1}
{0}"}<br />
----------<br />
Controller /people/3<br />
{"id":3,"givenName":"Leela","familyName":"Turanga","startDate":"1999-03-28T00:00:00-08:00","rating":8,"formatString":"{1}
{0}"}<br />
----------</code>
<div><br /></div>
<div>So let's change some settings.</div>
<div><br /></div>
<h2 style="text-align: left;">"WriteIndented = true"</h2>
<div>
So let's add the options setting to both projects (<a href="https://github.com/jeremybytes/controller-vs-minimal-apis/blob/main/DemoCode/MinimalApi/Program.cs">MinimalApi/Program.cs</a>
and
<a href="https://github.com/jeremybytes/controller-vs-minimal-apis/blob/main/DemoCode/ControllerApi/Program.cs">ControllerApi/Program.cs</a>):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPbRFnldPVw5Tq1GU6R_UKc8eePhiRDddXTVa-1KzRGwOnNU-JRSTSBl7mJfsoACdzgwaWbeuolIMk6VOs2mrrkPPVmOvbf6bU6UR2u4sUAmKd2bGVSMQLsFGpeG1pFZtL4hAGdZIKsncAMRT8R1n8VRGNTXNCduHpBY-E-mQUiIqLDK_L_in5hXIDxLA/s1270/01-setting-options.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="148" data-original-width="1270" height="74" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPbRFnldPVw5Tq1GU6R_UKc8eePhiRDddXTVa-1KzRGwOnNU-JRSTSBl7mJfsoACdzgwaWbeuolIMk6VOs2mrrkPPVmOvbf6bU6UR2u4sUAmKd2bGVSMQLsFGpeG1pFZtL4hAGdZIKsncAMRT8R1n8VRGNTXNCduHpBY-E-mQUiIqLDK_L_in5hXIDxLA/w640-h74/01-setting-options.png" width="640" /></a>
</div>
<br />
<code>
<pre> // Set JSON indentation
builder.Services.ConfigureHttpJsonOptions(
options => options.SerializerOptions.WriteIndented = true);
</pre>
</code>
<div><br /></div>
<div>
Then we just need to restart both services and rerun the ServiceTester
application:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDMQ-GIdB_hVUVr2qFb5JS_GVQp0NkK7gbSEyGGR-kOX6lfTA6ShFlzlRkM6-LSRFn90ed2VOj5QtEif1mSyrsWSn5Tj6JKSuos5CuMcQpE7E7fvqosgXV1ieuC377NAZ6TzpGftjYPTK-1Ua5uGjL6I48EW-n0DDUt7QiVEIvovPvVr7HiDINPcvmy4k/s1564/07-updated-output.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="795" data-original-width="1564" height="326" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDMQ-GIdB_hVUVr2qFb5JS_GVQp0NkK7gbSEyGGR-kOX6lfTA6ShFlzlRkM6-LSRFn90ed2VOj5QtEif1mSyrsWSn5Tj6JKSuos5CuMcQpE7E7fvqosgXV1ieuC377NAZ6TzpGftjYPTK-1Ua5uGjL6I48EW-n0DDUt7QiVEIvovPvVr7HiDINPcvmy4k/w640-h326/07-updated-output.png" width="640" /></a>
</div>
<br />
<code>
<pre> Minimal /people/3
{
"id": 3,
"givenName": "Leela",
"familyName": "Turanga",
"startDate": "1999-03-28T00:00:00-08:00",
"rating": 8,
"formatString": "{1} {0}"
}
----------
Controller /people/3
{"id":3,"givenName":"Leela","familyName":"Turanga","startDate":
"1999-03-28T00:00:00-08:00","rating":8,"formatString":"{1} {0}"}
----------</pre>
</code>
<div>
This shows us that the pretty formatting is applied to the Minimal API output,
but not to the Controller API output.
</div>
<div><br /></div>
<h2 style="text-align: left;">So What's Going On?</h2>
<div>
I found out about this new setting the same way as the one in the
<a href="https://jeremybytes.blogspot.com/2024/02/method-injection-in-aspnet-core-api.html">previous article</a>
-- by hearing about it in a conference session. I immediately gave it a try
and saw that it did not work on the API I was experimenting with. After few
more tries, I figured out that the setting worked with the Minimal APIs that I
had, but not the Controller APIs. (And I did try to get it to work with the
Controller APIs in a whole bunch of different ways.)
</div>
<div><br /></div>
<div>
Eventually, I came across this snippet in the documentation for the
<a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection.httpjsonserviceextensions.configurehttpjsonoptions?view=aspnetcore-8.0">"ConfigureHttpJsonOptions" method</a> (bold is mine):
</div>
<blockquote>
<div>
Configures options used for reading and writing JSON when using
Microsoft.AspNetCore.Http.HttpRequestJsonExtensions.ReadFromJsonAsync and
Microsoft.AspNetCore.Http.HttpResponseJsonExtensions.<b>WriteAsJsonAsync</b>.
JsonOptions uses default values from JsonSerializerDefaults.Web.
</div>
</blockquote>
<div>
This tells us that these options are only used in certain circumstances. In
this case, specifically when WriteAsJsonAsync is called.
</div>
<div><br /></div>
<div>
I have not been able to find what uses "WriteAsJsonAsync" and what does not.
Based on my observations, I assume that Minimal APIs <b>do</b> use
WriteAsJsonAsync and Controller APIs <b>do not</b> use WriteAsJsonAsync.
</div>
<div><br /></div>
<div>
So I add another item to my list of differences between Minimal APIs and
Controller APIs. I guess it's time to publish this list somewhere.
</div>
<div><br /></div>
<h2 style="text-align: left;">Wrap Up</h2>
<div>So, back to the beginning:</div>
<blockquote>
<span style="font-size: large;">ConfigureHttpJsonOptions <b>does</b> work for Minimal APIs.
<br />ConfigureHttpJsonOptions <b>does not</b> work for Controller
APIs. </span>
</blockquote>
<div>
It would be really nice if these capabilities matched or if there was a note
somewhere that said "This is for minimal APIs".
</div>
<div><br /></div>
<div>
Hopefully this will save you a bit of frustration in your own code. If you
want to use "WriteIntented = true", then you will need to use Minimal APIs
(not Controller APIs).
</div>
<div><br /></div>
<div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com2tag:blogger.com,1999:blog-5359546512544809971.post-80287341179493322112024-02-21T10:42:00.000-08:002024-02-22T07:32:15.188-08:00Method Injection in ASP.NET Core: API Controllers vs. MVC Controllers<div>
Method Injection in ASP.NET Core got a little bit easier in .NET 7. Before
.NET 7, we had to use the [FromServices] attribute on a method parameter in
order for the parameter to be injected from the services collection / dependency injection container. Starting
with .NET 7, the [FromServices] parameter became optional,
<b>but only in some places</b>.
</div>
<div><br /></div>
<div>Short Version:</div>
<blockquote>
<div>
<span style="font-size: large;">[FromServices] is <b>no longer required</b> for API
Controllers. </span>
</div>
<div>
<span style="font-size: large;">[FromServices] is <b>still required</b> for MVC Controllers.</span>
</div>
</blockquote>
<div>
The reason I'm writing about this is that I had a bit of confusion when I
first heard about it, and I suspect others may have as well. If you'd like
more details, and some samples, keep reading.
</div><div><br /></div>
<h2 style="text-align: left;">Confusion</h2>
<div>
I first heard about [FromServices] being optional from a conference talk on
ASP.NET Core. The speaker said that [FromServices] was no longer required for
method injection and removed it from the sample code to show it still worked.
</div>
<div><br /></div>
<div>
Now ASP.NET Core is not one of my focus areas, but I do have some sample apps that use method injection. When I tried this out for
myself on an MVC controller, it did not work. After a bit of digging (and
conversation with the speaker), we found that the change only applied to API
controllers.
</div>
<div><br /></div>
<div>
Let's look at some samples. This code shows a list of data from a single
source -- either through an API or displayed in an MVC view. (Sample code is
available here:
<a href="https://github.com/jeremybytes/method-injection-aspnetcore">https://github.com/jeremybytes/method-injection-aspnetcore</a>)
</div>
<div><br /></div>
<h2 style="text-align: left;">Data and Service Configuration</h2>
<div>
Both projects use the same library code for the data. This code is in the
<a href="https://github.com/jeremybytes/method-injection-aspnetcore/tree/main/DemoCode/People.Library">"People.Library" folder/project</a>.
</div>
<div><br /></div>
<div>IPeopleProvider:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhv0hkWwD-C_-zeUoreZVxHrSqxPSsL1BHYcFNmgEjxGaDzocRQ9mrQsB_p8ggqRp8olXKapUqhk4ETnL-M6CgfqB_s-9j9nQiCZSZ0OFtKwNsBeX4GSnheMlmffuJQXK1T6ZDUb3uiOuYuNvZz9yR1clYpPg8ass-BryxX2JvBbTEWyB5u4N4Ss4ktgEg/s742/01-provider-interface.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="234" data-original-width="742" height="202" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhv0hkWwD-C_-zeUoreZVxHrSqxPSsL1BHYcFNmgEjxGaDzocRQ9mrQsB_p8ggqRp8olXKapUqhk4ETnL-M6CgfqB_s-9j9nQiCZSZ0OFtKwNsBeX4GSnheMlmffuJQXK1T6ZDUb3uiOuYuNvZz9yR1clYpPg8ass-BryxX2JvBbTEWyB5u4N4Ss4ktgEg/w640-h202/01-provider-interface.png" width="640" /></a>
</div>
<code>
<pre> public interface IPeopleProvider
{
Task<List<Person>> GetPeople();
Task<Person?> GetPerson(int id);
}</pre>
</code>
<div><br /></div>
<div>
The method we care about is the "GetPeople" method that returns a list of
"Person" objects. There is also a "HardCodedPeopleProvider" class in the same project that
implements the interface. This returns a hard-coded list of objects.
</div>
<div><br /></div>
<div>
Both the API project and the MVC project configure the "IPeopleProvider" the
same way. (API code:
<a href="https://github.com/jeremybytes/method-injection-aspnetcore/blob/main/DemoCode/ControllerApi/Program.cs">ControllerAPI/Program.cs</a>
-- MVC code:
<a href="https://github.com/jeremybytes/method-injection-aspnetcore/blob/main/DemoCode/PeopleViewer/Program.cs">PeopleViewer/Program.cs</a>)
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieRT21siushHbUoJb4Bbmb1_T6nr2mxUMlZ8W_ZjseLoLH_zzk1hxsDiFDGWRsORCLoZ01DguFrRaE352EYZJC1oThPGOrEnwIxw3Wgr7KrsCx_i4rUv5gzBcgs1c9xEVxLiL7815hhWiDF-3_sSKqZQ5mk3WXrM0UYPftrJvI1rj6l-6L6Zga1Z9PcTU/s1447/02-service-configuration.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="111" data-original-width="1447" height="50" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEieRT21siushHbUoJb4Bbmb1_T6nr2mxUMlZ8W_ZjseLoLH_zzk1hxsDiFDGWRsORCLoZ01DguFrRaE352EYZJC1oThPGOrEnwIxw3Wgr7KrsCx_i4rUv5gzBcgs1c9xEVxLiL7815hhWiDF-3_sSKqZQ5mk3WXrM0UYPftrJvI1rj6l-6L6Zga1Z9PcTU/w640-h50/02-service-configuration.png" width="640" /></a>
</div>
<br />
<code>
<pre> // Add services to the container.
builder.Services.AddScoped<IPeopleProvider, HardCodedPeopleProvider>();</pre>
</code>
<div><br /></div>
<div>
This registers the HardCodedPeopleProvider with the dependency injection
container so that we can inject it into our controllers.
</div><div><br /></div>
<h2 style="text-align: left;">API Controller</h2>
<div><b>Using [FromServices]</b></div>
<div>
Prior to .NET 7, we could inject something from our dependency injection
container by using the [FromServices] attribute. Here's what that code looks
like in the API controller (from the
<a href="https://github.com/jeremybytes/method-injection-aspnetcore/blob/main/DemoCode/ControllerApi/Controllers/PeopleController.cs">"PeopleController.cs" file</a>)
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq-Cgt5ltFfju-txCvUWJUYX8bVqJAkbJdH_YuFhosbFVCFDumTLm3X_Re3Sey70YSPbyyfBA2LhwPNFAAlgiNbphW36Bzl9_MWTw8lFsWhR1KbTkL2bbeEUb2Efu28gnOZWItvrc0hxtcszkubZhNTH-scmb6BpJuwCHJUNoazEAd_k0i4WI77lhAD98/s918/03-api-fromservices.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="280" data-original-width="918" height="196" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq-Cgt5ltFfju-txCvUWJUYX8bVqJAkbJdH_YuFhosbFVCFDumTLm3X_Re3Sey70YSPbyyfBA2LhwPNFAAlgiNbphW36Bzl9_MWTw8lFsWhR1KbTkL2bbeEUb2Efu28gnOZWItvrc0hxtcszkubZhNTH-scmb6BpJuwCHJUNoazEAd_k0i4WI77lhAD98/w640-h196/03-api-fromservices.png" width="640" /></a>
</div>
<code>
<pre> [HttpGet(Name = "GetPeople")]
public async Task<IEnumerable<Person>> Get(
[FromServices] IPeopleProvider provider)
{
return await provider.GetPeople();
}</pre>
</code>
<div><br /></div>
<div>
When we call this method, the "HardCodedPeopleProvider" is automatically used for the "provider" parameter.
</div>
<div><br /></div>
<div>The result is the following:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgB8B_WHwuXAGZwjOenqFMf4e5uv14ejn0kOFcuUqUwS4k5jZ3FdJJFKfGnB7AGrwE_TbluEdderXN0mV0Mb1gUjF_4dySJGvUsk-7f1fOmPepGbpdYF6cywdy3kheAIuzW9BArcp_GHDmEeTXRL4Okada93WLtlRUPiWeEC3YpFjOOZ3zeTvLOIe4edEs/s1387/04-api-output1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="702" data-original-width="1387" height="324" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgB8B_WHwuXAGZwjOenqFMf4e5uv14ejn0kOFcuUqUwS4k5jZ3FdJJFKfGnB7AGrwE_TbluEdderXN0mV0Mb1gUjF_4dySJGvUsk-7f1fOmPepGbpdYF6cywdy3kheAIuzW9BArcp_GHDmEeTXRL4Okada93WLtlRUPiWeEC3YpFjOOZ3zeTvLOIe4edEs/w640-h324/04-api-output1.png" width="640" /></a>
</div>
<div><br /></div>
<code><div>
[{"id":1,"givenName":"John","familyName":"Koenig","startDate":"1975-10-17T00:00:00-07:00","rating":6,"formatString":""},{"id":2,"givenName":"Dylan","familyName":"Hunt","startDate":"2000-10-02T00:00:00-07:00","rating":8,"formatString":""},...,{"id":14,"givenName":"Devon","familyName":"","startDate":"1973-09-23T00:00:00-07:00","rating":4,"formatString":"{0}"}]
</div></code>
<div><br /></div>
<div><i>
Note: I'm sorry about the JSON formatting. "SerializerOptions.WriteIndented"
is a topic for another day. <br /><b>Update</b>: Here's an article on that: <a href="https://jeremybytes.blogspot.com/2024/02/minimal-apis-vs-controller-apis.html">Minimal APIs vs Controller APIs: SerializerOptions.WriteIndented = true</a>.</i></div>
<div><br /></div>
<div><b>Removing [FromServices]</b></div>
<div>As noted, the "FromServices" is now optional:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-cQPzlO8uoPgtRl6CesGzf6BdsbPnuRgeNxP3NltLPxH5aIUbdetgLbeYLHbC9nYCShipBDWf_ZIXyPRvhRzn7jCpr2t7gbc1ndycyOgV_JvtEbZgPv84K55_087rfXzhA1Nsbl0W2Eo-ogwsiU2eUeDekpKCDjLWT-jGZWHruLLsPYgFgSy5c_aHvZ0/s909/05-api-optional.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="292" data-original-width="909" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-cQPzlO8uoPgtRl6CesGzf6BdsbPnuRgeNxP3NltLPxH5aIUbdetgLbeYLHbC9nYCShipBDWf_ZIXyPRvhRzn7jCpr2t7gbc1ndycyOgV_JvtEbZgPv84K55_087rfXzhA1Nsbl0W2Eo-ogwsiU2eUeDekpKCDjLWT-jGZWHruLLsPYgFgSy5c_aHvZ0/w640-h206/05-api-optional.png" width="640" /></a>
</div>
<div><br /></div>
<code>
<pre> [HttpGet(Name = "GetPeople")]
public async Task<IEnumerable<Person>> Get(
IPeopleProvider provider)
{
return await provider.GetPeople();
}</pre>
</code>
<br />
<div>The output is the same as we saw above:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgB8B_WHwuXAGZwjOenqFMf4e5uv14ejn0kOFcuUqUwS4k5jZ3FdJJFKfGnB7AGrwE_TbluEdderXN0mV0Mb1gUjF_4dySJGvUsk-7f1fOmPepGbpdYF6cywdy3kheAIuzW9BArcp_GHDmEeTXRL4Okada93WLtlRUPiWeEC3YpFjOOZ3zeTvLOIe4edEs/s1387/04-api-output1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="702" data-original-width="1387" height="324" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgB8B_WHwuXAGZwjOenqFMf4e5uv14ejn0kOFcuUqUwS4k5jZ3FdJJFKfGnB7AGrwE_TbluEdderXN0mV0Mb1gUjF_4dySJGvUsk-7f1fOmPepGbpdYF6cywdy3kheAIuzW9BArcp_GHDmEeTXRL4Okada93WLtlRUPiWeEC3YpFjOOZ3zeTvLOIe4edEs/w640-h324/04-api-output1.png" width="640" /></a>
</div>
<div><br /></div>
<code><div>
[{"id":1,"givenName":"John","familyName":"Koenig","startDate":"1975-10-17T00:00:00-07:00","rating":6,"formatString":""},{"id":2,"givenName":"Dylan","familyName":"Hunt","startDate":"2000-10-02T00:00:00-07:00","rating":8,"formatString":""},...,{"id":14,"givenName":"Devon","familyName":"","startDate":"1973-09-23T00:00:00-07:00","rating":4,"formatString":"{0}"}]</div></code>
<blockquote>
<div>
<span style="font-size: large;">[FromServices] is <b>no longer required</b> for API
Controllers.</span></div>
</blockquote>
<div>Now let's take a look at an MVC controller.</div>
<div><br /></div>
<h2 style="text-align: left;">MVC Controller</h2>
<div><b>Using [FromServices]</b></div>
<div>
The [FromServices] attribute lets us use method injection in MVC controllers
as well. Here is an action method from the "PeopleViewer" project (from the
<a href="https://github.com/jeremybytes/method-injection-aspnetcore/blob/main/DemoCode/PeopleViewer/Controllers/PeopleController.cs">"PeopleController.cs" file)</a>:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1OhUPQTld1XEPVki1gYS6Fb0Yvf8CR1yeB8PcQEpiH7Q8oynMTJhyMWV4HOtnyh-pNAR6nsaBykMMD0f2hNJv2ukDFJspMgI2ZXy_MA93REF_xzu26E0UgZd6f5yv4Neylujt6bh_E3cbrsnl1FrtAk7YV_JY95huKTF5QIHjfCc_I8OGZMK-xb_Xq4c/s1212/06-mvc-fromservices.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="412" data-original-width="1212" height="218" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1OhUPQTld1XEPVki1gYS6Fb0Yvf8CR1yeB8PcQEpiH7Q8oynMTJhyMWV4HOtnyh-pNAR6nsaBykMMD0f2hNJv2ukDFJspMgI2ZXy_MA93REF_xzu26E0UgZd6f5yv4Neylujt6bh_E3cbrsnl1FrtAk7YV_JY95huKTF5QIHjfCc_I8OGZMK-xb_Xq4c/w640-h218/06-mvc-fromservices.png" width="640" /></a>
</div>
<br />
<code>
<pre> public async Task<IActionResult> GetPeople(
[FromServices] IPeopleProvider provider)
{
ViewData["Title"] = "Method Injection";
ViewData["ReaderType"] = provider.GetType().ToString();
var people = await provider.GetPeople();
return View("Index", people);
}</pre>
</code>
<div><br /></div>
<div>
As with the API controller, the "HardCodedPeopleProvider" is automatically passed for the method parameter. Here is the output of the view:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuDIC0i4F8TNZTLXvmz9cyPYXr4bHRqPpgIRXOtJU1uu89Ow3rLRXeXiB6kG_3w-WYTQN4T9xU2zuN-MCqFaD5b3vpfFYqVFN8KuFdsEka09uWHg6G_9VixJ57Z7kbDytV5ioLNBy6SRfGAaPqqGsCwID5a0SoM0hCc2J3raprlTeWpMbpIeAq4MCtRcY/s1299/07-mvc-output1.png" style="margin-left: 1em; margin-right: 1em;"><img alt="Web browser showing output of People objects in a grid. Example item is "John Koenig 1975 6/10 Stars"" border="0" data-original-height="1110" data-original-width="1299" height="546" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhuDIC0i4F8TNZTLXvmz9cyPYXr4bHRqPpgIRXOtJU1uu89Ow3rLRXeXiB6kG_3w-WYTQN4T9xU2zuN-MCqFaD5b3vpfFYqVFN8KuFdsEka09uWHg6G_9VixJ57Z7kbDytV5ioLNBy6SRfGAaPqqGsCwID5a0SoM0hCc2J3raprlTeWpMbpIeAq4MCtRcY/w640-h546/07-mvc-output1.png" width="640" /></a>
</div>
<br />
<div><br /></div>
<div>
This shows the same data as the API in a colorful grid format (as a side note,
the background color of each item corresponds with the decade of the date.)
</div>
<div><br /></div>
<div><b>Removing [FromServices]</b></div>
<div>
Because we can remove [FromServices] from the API controller, I would guess
that we can remove it from the MVC controller as well.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCula3AO641kV0FtoXA7Kztk28_EZ7FxxQNQioalJsHc8KohwY7r-_I1JoHj6_favNZXKEf6Icl1ZKfQaACOSGQ3RHuvLleiSxXE1cnHrF7gHGzZmHO42nOP0nf6MT1zTsJd1UeELMu0byBCalEfjzC9lavx0oDruGffMGKJVBLDiUS48zhWoZY0UwegU/s1213/08-mvc-noattribute.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="406" data-original-width="1213" height="214" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCula3AO641kV0FtoXA7Kztk28_EZ7FxxQNQioalJsHc8KohwY7r-_I1JoHj6_favNZXKEf6Icl1ZKfQaACOSGQ3RHuvLleiSxXE1cnHrF7gHGzZmHO42nOP0nf6MT1zTsJd1UeELMu0byBCalEfjzC9lavx0oDruGffMGKJVBLDiUS48zhWoZY0UwegU/w640-h214/08-mvc-noattribute.png" width="640" /></a>
</div>
<br />
<code>
<pre> public async Task<IActionResult> GetPeople(
IPeopleProvider provider)
{
ViewData["Title"] = "Method Injection";
ViewData["ReaderType"] = provider.GetType().ToString();
var people = await provider.GetPeople();
return View("Index", people);
}</pre>
</code>
<div><br /></div>
<div>However, when we run this application, we get a runtime exception:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy6pE88XeKzQAbJi6pXraaSWa3RIJr_si-nn-ihnZy-BL_ok4ATLTba7Qrq221qqWw6UF_n_ENzDAZRWKVdkVUn3h3nJWZ5kU2n5Heg-DUXNsTENwTs8KmCdBpiAkdffbT2edxmn1SjIugD5UYmGUg39uTp1QG4O88iBe-hp5KDJ7l57IEID9aDGklSaE/s1311/09-mvc-output2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1014" data-original-width="1311" height="496" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy6pE88XeKzQAbJi6pXraaSWa3RIJr_si-nn-ihnZy-BL_ok4ATLTba7Qrq221qqWw6UF_n_ENzDAZRWKVdkVUn3h3nJWZ5kU2n5Heg-DUXNsTENwTs8KmCdBpiAkdffbT2edxmn1SjIugD5UYmGUg39uTp1QG4O88iBe-hp5KDJ7l57IEID9aDGklSaE/w640-h496/09-mvc-output2.png" width="640" /></a>
</div>
<br />
<code>InvalidOperationException: Could not create an instance of type
'People.Library.IPeopleProvider'. Model bound complex types must not be
abstract or value types and must have a parameterless constructor. Record
types must have a single primary constructor. Alternatively, give the
'provider' parameter a non-null default value.</code>
<div><br /></div>
<div>
ASP.NET Core MVC does not automatically look in the dependency injection
container for parameters that it cannot otherwise bind. So, without the
[FromServices] attribute, this method fails.
</div>
<blockquote>
<div><span style="font-size: x-large;">[FromServices] is </span><b style="font-size: x-large;">still required</b><span style="font-size: x-large;"> for MVC Controllers.</span></div></blockquote>
<h2 style="text-align: left;">Messaging and Documentation Frustrations</h2>
<div>If you've read this far, then you're looking for a bit more than just the "Here's how things are", so I'll give a bit of my opinions and frustrations.</div><div><br /></div><div>
I've had a problem with Microsoft's messaging for a while now. It
seems like they are very good at saying "Look at this cool new thing" without
mentioning how it differs from the old thing or what is
<b>not</b> impacted by the new thing. (I had a similar experience with
minimal APIs and behavior that is different from controller APIs but not
really called out anywhere in the documentation: <a href="https://jeremybytes.blogspot.com/2022/04/returning-http-204-no-content-from-net.html">Returning HTTP 204 (No Content) from .NET Minimal API</a>.)
</div><div><br /></div><div>I see the same thing happening with [FromServices].</div><div><br /></div><div>Here is the documentation (from "<a href="https://learn.microsoft.com/en-us/aspnet/core/release-notes/aspnetcore-7.0?view=aspnetcore-8.0#api-controllers">What's New in ASP.NET Core 7.0</a>")</div><blockquote><div><div><b>Parameter binding with DI in API controllers</b></div><div>Parameter binding for API controller actions binds parameters through dependency injection when the type is configured as a service. This means it's no longer required to explicitly apply the [FromServices] attribute to a parameter.</div></div></blockquote>
<div>Unfortunately, the "hype" shortens this message:</div><blockquote><div>[FromServices] is no longer required.</div></blockquote><div>Please don't write to me and say "It's obvious from the documentation this only applies to API controllers. There is no reason to believe it would apply to anything else." This is easy to say when you already know this. But what about if you don't know?</div><div><br /></div><div>Let's look at it from an average developer's perspective. They have used API controllers (and may be using minimal APIs) and they have used MVC controllers. The methods and behaviors of these controllers are very similar: parameter mapping, routing, return types, and other bits. It is not a very far leap to assume that changes to how a controller works (whether API or MVC) would apply in both scenarios.</div><div><br /></div><div>As noted above, the speaker I originally heard this from did not realize the limitations at the time, and this speaker is an expert in ASP.NET Core (including writing books and teaching multi-day workshops). They had missed the distinction as well.</div><div><br /></div><div>And unfortunately, the documentation is lacking (at least as of the writing of this article in Feb 2024): </div><div><br /></div><div><div><ul style="text-align: left;"><li>The <a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.fromservicesattribute?view=aspnetcore-8.0">"FromServicesAttribute" documentation</a> does not have any usage notes that would indicate that it is required in some places and optional in others. This is the type of note I expect to see (as an example the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.processstartinfo.useshellexecute?view=net-8.0">UseShellExecute</a> default value changed between .NET Framework and .NET Core, and it is noted in the language doc.)</li><li>The <a href="https://learn.microsoft.com/en-us/aspnet/core/mvc/overview?view=aspnetcore-8.0">Overview of ASP.NET Core MVC</a> documentation does have a "Dependency Injection" topic that mentions method injection. However, the corresponding <a href="https://learn.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-8.0">Create web APIs with ASP.NET Core</a> documentation does not have a "Dependency Injection" topic.</li></ul></div></div><div><br /></div><h2 style="text-align: left;">Stop Complaining and Fix It, Jeremy</h2><div>I don't like to rant without having a solution. But this has been building up for a while now. The obvious answer is "Documentation is open source. Anyone can contribute. Update the articles, Jeremy."</div><div><br /></div><div>If you know me, then you know that I have a passion for helping people learn about interfaces and how to use them appropriately. If I have "expertise" in any part of the C# language, it is interfaces. When it came to a set of major changes (specifically C# 8), I looked into them and wrote about them (<a href="https://jeremybytes.blogspot.com/2019/09/a-closer-look-at-c-8-interfaces.html">A Closer Look at C# 8 Interfaces</a>), and I have also spoken quite a bit about those changes: <a href="https://github.com/jeremybytes/csharp11-interfaces">Caching Up with C# Interfaces, What You Know may be Wrong</a>.</div><div><br /></div><div>I have noted that the documentation is lacking in a lot of the areas regarding the updates that were made to interfaces. So why haven't I contributed?</div><div><blockquote>I do not have enough information to write the documentation.</blockquote></div><div>I can write about my observations. I can point to things that look like bugs to me. But I do not know what the feature is supposed to do. It may be working as intended (as I have discovered about "bugs" I have come across in the past). I am not part of the language team; I am not part of the documentation team; I do not have access to the resources needed to correctly document features.</div><div><br /></div><h2 style="text-align: left;">Wrap Up</h2><div>I will apologize for ranting without a solution. I was a corporate developer for many years. I understand the pressures of having to get code completed. I see how difficult it is to keep up with changing environments while releasing applications. I know what it is like to support multiple applications written over a number of years that may or may not take similar approaches.</div><div><br /></div><div>I have a love and concern for the developers working in those environments. There are times when I have difficulty keeping up with things (and keeping up with things is a big part of my job). If I have difficulty with this, what chance does the average developer have? Something has got to give.</div><div><br /></div><div>Anyway, I have some more posts coming up about some other things that I've come across. Hopefully those will be helpful. (No ranting, I promise.)</div><div><br /></div><div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-62052547730770061532023-10-06T13:00:00.169-07:002023-10-06T13:17:27.430-07:00Producer/Consumer Exception Handling - A More Reliable Approach<div>
In the last 2 articles, we looked at a couple of ways to deal with exceptions
that can happen when using the Producer/Consumer pattern along with
Channel<T> in C#. (Prior articles: "<a href="https://jeremybytes.blogspot.com/2023/10/dont-use-taskwhenall-for-interdependent.html">Don't Use 'Task.WhenAll' for Interdependent Tasks</a>" and "<a href="https://jeremybytes.blogspot.com/2023/10/looking-at-producerconsumer.html">Looking at Producer/Consumer Dependencies: Bounded vs. Unbounded
Channels</a>".)
</div>
<div><br /></div>
<div>
Both of these approaches are short-circuiting, meaning that if the Consumer
fails on one item, no further items are processed. The same is true of the
Producer: if the Producer fails on one item, no further items are produced.
This approach may work fine for particular applications. But what if we want
something more robust?</div>
<div>
<blockquote>
<span style="font-size: large;"><b>By handling exceptions inside the Producer and Consumer, errors can be
logged and processing can continue.</b></span>
</blockquote>
</div>
<div>
We can go back and look at the logged items to determine which items need to
be redone. This is a more robust approach to exception handling for our sample
application.
</div>
<div><br /></div>
<h2 style="text-align: left;">Motivation</h2>
<div>
This article is based on a question sent in by Edington
Watt regarding a presentation that I did about
<a href="https://github.com/jeremybytes/csharp-channels-presentation">Channel<T> in C#</a>. He and the WTW ICT Technology team wrote a sample project that showed
strange behavior when exceptions are thrown, and he asked the question "What
are the best exception handling practices in the Consumer Producer approach
using Channel<T>?"
</div>
<div><br /></div>
<div>
Today we'll look at handling exceptions in a way that does not short-circuit
the process.
</div>
<div><br /></div>
<div>
<i>Note: The full source code (including the original sample code and various
approaches to fixing the strange behavior is available here: <a href="https://github.com/jeremybytes/channel-exceptions">https://github.com/jeremybytes/channel-exceptions</a>.</i>
</div>
<div><br /></div>
<div><b>Articles</b></div>
<div>
<ul style="text-align: left;">
<li><a href="https://jeremybytes.blogspot.com/2023/10/dont-use-taskwhenall-for-interdependent.html">Don't Use "Task.WhenAll" for Interdependent Tasks</a></li>
<li><a href="https://jeremybytes.blogspot.com/2023/10/looking-at-producerconsumer.html">Looking at Producer/Consumer Dependencies: Bounded vs. Unbounded Channels</a></li>
<li>Producer/Consumer Exception Handling - A More Reliable Approach <i>(this one)</i></li>
</ul>
</div>
<div><br /></div>
<h2 style="text-align: left;">Interdependent Tasks</h2>
<div>
We'll go back to the starting place we used in the previous article. Here are
the relevant methods (available in the
<a href="https://github.com/jeremybytes/channel-exceptions/blob/main/ChannelExceptions/refactored/Program.cs">Program.cs file of the "refactored" project</a>):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik9EbU6x3hDnqCBOeJw5q2CN8OIILEqPxTGu-ycX8nnjKE6OI7r6Y8qWSeLJlW7p9LqqNP2lYTxx_DKH8AGZXSWZeOeQAYVsuwnWPngMU38Dq-8avwiXqnqyyH1G5kanmRTbP53lWfWjMnHc6zbZclWVKpZoovdEoyEDkTWyYxgP-SXTzDLpYvzJZZ280/s872/Starting_Code.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="642" data-original-width="872" height="472" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik9EbU6x3hDnqCBOeJw5q2CN8OIILEqPxTGu-ycX8nnjKE6OI7r6Y8qWSeLJlW7p9LqqNP2lYTxx_DKH8AGZXSWZeOeQAYVsuwnWPngMU38Dq-8avwiXqnqyyH1G5kanmRTbP53lWfWjMnHc6zbZclWVKpZoovdEoyEDkTWyYxgP-SXTzDLpYvzJZZ280/w640-h472/Starting_Code.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> static async Task Main(string[] args)
{
try
{
await ProducerConsumerWithExceptions();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Done");
}
static async Task ProducerConsumerWithExceptions()
{
var channel = Channel.CreateBounded<int>(new BoundedChannelOptions(10));
Task producer = Producer(channel.Writer);
Task consumer = Consumer(channel.Reader);
await Task.WhenAll(producer, consumer);
}</code></pre>
<div><br /></div>
<div>
I won't go through this code again here. Check the
<a href="https://jeremybytes.blogspot.com/2023/10/dont-use-taskwhenall-for-interdependent.html">prior article for a walkthrough</a>. This code works fine for the success state, but it has strange behavior if
the Consumer throws an exception.
</div>
<div><br /></div>
<h3 style="text-align: left;">Consumer Exception</h3>
<div>
If the Consumer throws an exception while processing an item, the entire
application hangs:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg67S_4u0fjERre-oDk73RnDE4_QPEuYyBkrx03SIdooibmdBDJDuYxC21-45mvdrHD9Ro8mKXehqivS9bN-GxrieJF8Dihp7kBApYHOCaKkJb2ZW6NjE075MILMvpILa_U0y0L412huU0BdJDi3oFseTTPxfSAdOeCy415_xc6ktvU8Dz8GX6xDip4Dj8/s577/Original-ErrorOutput.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="577" data-original-width="481" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg67S_4u0fjERre-oDk73RnDE4_QPEuYyBkrx03SIdooibmdBDJDuYxC21-45mvdrHD9Ro8mKXehqivS9bN-GxrieJF8Dihp7kBApYHOCaKkJb2ZW6NjE075MILMvpILa_U0y0L412huU0BdJDi3oFseTTPxfSAdOeCy415_xc6ktvU8Dz8GX6xDip4Dj8/w334-h400/Original-ErrorOutput.png" width="334" /></a>
</div>
<br />
<pre><code>Producing something: 0
Producing something: 1
Consuming object: 0
Producing something: 2
Producing something: 3
Producing something: 4
Producing something: 5
Producing something: 6
Producing something: 7
Producing something: 8
Producing something: 9
Producing something: 10
Producing something: 11
</code></pre>
<div><br /></div>
<div>Here's a reminder of what happens:</div>
<div><br /></div>
<div>
In the "ProducerConsumerWithExceptions" method, we create a Bounded Channel.
This means that the capacity of the channel is limited to 10 items. When the
channel has reached that capacity, no new items can be written to it until
space is made available for them.
</div>
<div><br /></div>
<div>
The Consumer reads one item off of the channel (index 0) and then throws an
exception. The Consumer then stops, and no further items are read off of the
channel.
</div>
<div><br /></div>
<div>
The Producer produces data and puts it onto the channel. The first items
produced (index 0) is pulled off of the channel by the Consumer. The Producer
keeps going and puts 10 more items onto the channel (indexes 1 through 10).
</div>
<div><br /></div>
<div>
Then the Producer produces index 11 and tries to put it onto the channel. At
this point, the channel is at capacity (it has 10 items already), so the
Producer waits (in an async-friendly way) for there to be space to write the
next item.
</div>
<div><br /></div>
<div>
And this is where the application hangs. The Producer will wait forever. Since
"Task.WhenAll" is waiting for the Producer and Consumer to both finish, it
will also wait forever. The application will not exit on its own.
</div>
<div><br /></div>
<h2 style="text-align: left;">
No Handling of Exceptions in the Producer/Consumer
</h2>
<div>
One cause of the behavior is that the Producer and Consumer do not handle
their own exceptions. They let them bubble up and (hopefully) get caught in
the Main method's try/catch block.
</div>
<div><br /></div>
<h3 style="text-align: left;">Consumer</h3>
<div>
Here is the code for the Consumer (in the
<a href="https://github.com/jeremybytes/channel-exceptions/blob/main/ChannelExceptions/refactored/Program.cs">"Program.cs" file in the "refactored" project</a>):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMPfcZB31EF1Ca_69xL_nTBsvEN6cAWLh-1ao0WM6_erFTA2jdq6sE6DQsUewU8AWdle3HRtFeuQh6dIRI-1bqAvlWqiXvCXkjqurQp9OaE19Ms8goYsgIOAVYZr6fJ0o5ex4cNnEMX7v9uVAxDeKFe9JiTGzUXLz_oRWtwVA2WB79e_rglbee_Z4bp-s/s733/Original_Consumer.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="211" data-original-width="733" height="184" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMPfcZB31EF1Ca_69xL_nTBsvEN6cAWLh-1ao0WM6_erFTA2jdq6sE6DQsUewU8AWdle3HRtFeuQh6dIRI-1bqAvlWqiXvCXkjqurQp9OaE19Ms8goYsgIOAVYZr6fJ0o5ex4cNnEMX7v9uVAxDeKFe9JiTGzUXLz_oRWtwVA2WB79e_rglbee_Z4bp-s/w640-h184/Original_Consumer.png" width="640" /></a>
</div>
<br />
<pre><code> static async Task Consumer(ChannelReader<int> reader)
{
await foreach (var item in reader.ReadAllAsync())
{
Console.WriteLine($"Consuming object: {item}");
MightThrowExceptionForConsumer(item);
}
}</code></pre>
<div><br /></div>
<div>
This uses the "ReadAllAsync" method on the "ChannelWriter<T>" class to
read the items off of the Channel as they become available.
</div>
<div><br /></div>
<div>
The call to "MightThrowExceptionForConsumer" throws an exception. (Although it
has "might" in the name, it is hard-coded to always throw an exception here.)
And because the exception is unhandled, the "foreach" loop will exit.
</div>
<div><br /></div>
<div>
This relies on the exception eventually making its way back to the "Main"
method where it will be caught and logged in the catch block.
</div>
<div><br /></div>
<h3 style="text-align: left;">Producer</h3>
<div>
The Producer code is similar (in the same <a href="https://github.com/jeremybytes/channel-exceptions/blob/main/ChannelExceptions/refactored/Program.cs">"Program.cs" file in the "refactored" project</a>):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGlBagnQapXB12IkRqgZ1hpd9_gapc_dMwoMaOPBC1ZXoZGKwRROgOawB6zI389W41C2dLxJQCyhyKZs5QsjH2XHEGSQIYL-NdlA3I5YYhgh2_U05Oe6pQfKNp8Q_09ISBcQq1ocHYOkDGpOBfhNvdl-5H1z8_skk3o8KKZJymLXedNTinPS9s7VhFov4/s781/Original_Producer.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="455" data-original-width="781" height="372" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGlBagnQapXB12IkRqgZ1hpd9_gapc_dMwoMaOPBC1ZXoZGKwRROgOawB6zI389W41C2dLxJQCyhyKZs5QsjH2XHEGSQIYL-NdlA3I5YYhgh2_U05Oe6pQfKNp8Q_09ISBcQq1ocHYOkDGpOBfhNvdl-5H1z8_skk3o8KKZJymLXedNTinPS9s7VhFov4/w640-h372/Original_Producer.png" width="640" /></a>
</div>
<br />
<pre><code> static async Task Producer(ChannelWriter<int> writer)
{
try
{
for (int i = 0; i < 20; i++)
{
Console.WriteLine($"Producing something: {i}");
MightThrowExceptionForProducer(i);
await Task.Delay(10);
await writer.WriteAsync(i);
}
}
finally
{
writer.Complete();
}
}</code></pre>
<div><br /></div>
<div>
This uses a "for" loop to generate 20 items and write them to the Channel
using "WriteAsync". This could also throw an exception in the
"MightThrowExceptionForProducer" method (although for this sample, it is
hard-coded to not throw an exception).
</div>
<div><br /></div>
<div>
If an exception is thrown in the "for" loop, the loop will exit before
finishing. And although we do not have a "catch" block to catch an exception,
we do have a "finally" block.
</div>
<div><br /></div>
<div>
In the "finally" block we mark the ChannelWriter as "Complete". This lets the
Consumer know that no new items will be added to the Channel, and the Consumer
can stop waiting for new items. This is in a "finally" block because even if
we get an exception, we want to make sure that the ChannelWriter is marked
"Complete".
</div>
<div><br /></div>
<div>
But as with the Consumer, the Producer relies on the exception bubbling up to
the "Main" method where it should be logged in the "catch" block.
</div>
<div><br /></div>
<h2 style="text-align: left;">
Adding Exception Handling to the Producer and Consumer
</h2>
<div>
A more reliable way of dealing with exceptions is to handle them inside the
Producer and the Consumer. This also has the effect of removing the dependency between the Producer and Consumer that we saw earlier. With the dependency removed, we can use a Bounded Channel if we need to, and we can also use "Task.WhenAll" to wait for the Producer and Consumer tasks to complete.</div><div><br /></div><div><i>The code for this section is available in the "doesnt-stop"
project.
</i></div>
<div><br /></div>
<h3 style="text-align: left;">Consumer</h3>
<div>
Here is the code for the updated Consumer (in the
<a href="https://github.com/jeremybytes/channel-exceptions/blob/main/ChannelExceptions/doesnt-stop/Program.cs">"Program.cs" file in the "doesnt-stop" project</a>):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiieXai46Z6xRG54FDChm1K6OWb7_n6DCfntJaK-SjrNfeXHzYlZx2xK1ODy63gXGg6LzCBc1SCQjU_uLqZ0M4xOt9T5TnwBtsw41eYuZUDCocNiQGmX7wczue8y6MnewCxqSSGum5XbhrUh6YrnUKxyeQKOun19Cest9J4hkBC1LAOPD2NBxHJNvDrGgE/s780/Updated_Consumer.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="428" data-original-width="780" height="352" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiieXai46Z6xRG54FDChm1K6OWb7_n6DCfntJaK-SjrNfeXHzYlZx2xK1ODy63gXGg6LzCBc1SCQjU_uLqZ0M4xOt9T5TnwBtsw41eYuZUDCocNiQGmX7wczue8y6MnewCxqSSGum5XbhrUh6YrnUKxyeQKOun19Cest9J4hkBC1LAOPD2NBxHJNvDrGgE/w640-h352/Updated_Consumer.png" width="640" /></a>
</div>
<pre><code> static async Task Consumer(ChannelReader<int> reader)
{
await foreach (var item in reader.ReadAllAsync())
{
try
{
Console.WriteLine($"Consuming object: {item}");
MightThrowExceptionForConsumer(item);
TotalConsumed++;
}
catch (Exception ex)
{
Console.WriteLine($"Logged: {ex.Message}");
}
}
}</code></pre>
<div><br /></div>
<div>
Here we have added a try/catch block <b>inside</b> of the foreach loop.
This means that if an exception is thrown, it will be caught and logged here,
and the loop will continue to the next item.
</div>
<div><br /></div>
<div>
The Consumer is no longer short-circuiting. So we do no longer need to worry
about the Consumer stopping before it has read all of the items from the
Channel.
</div>
<div><br /></div>
<div>
One other new item is "TotalConsumed++". This is a static variable that is
updated with the total number of items successfully processed (it is after the
"MightThrow..." method). This lets us track how many items were successful in
the Consumer.
</div>
<div><br /></div>
<h3 style="text-align: left;">Producer</h3>
<div>
We have something very similar in the Producer (in the <a href="https://github.com/jeremybytes/channel-exceptions/blob/main/ChannelExceptions/doesnt-stop/Program.cs">"Program.cs" file in the "doesnt-stop" project</a>):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLcgwui2Oea4SL2Lv9pQ7MPVCorpAK_mjAq-TBdabQdZFqu1AGt72ZTcrPRwOqwt3TWUUEoRF7VUd4ZOPhvT6NhNhpCfreET9jDrSMajn3L3e9FnS83v9JewkrXO5SA0lbMhSNbkB-nmzGUW5oK7MDYjWbqKzq7sbhVZjiyoWo2iyogfQKTLTOtpenBF8/s787/Updated_Producer.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="537" data-original-width="787" height="436" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLcgwui2Oea4SL2Lv9pQ7MPVCorpAK_mjAq-TBdabQdZFqu1AGt72ZTcrPRwOqwt3TWUUEoRF7VUd4ZOPhvT6NhNhpCfreET9jDrSMajn3L3e9FnS83v9JewkrXO5SA0lbMhSNbkB-nmzGUW5oK7MDYjWbqKzq7sbhVZjiyoWo2iyogfQKTLTOtpenBF8/w640-h436/Updated_Producer.png" width="640" /></a>
</div>
<pre><code> static async Task Producer(ChannelWriter<int> writer)
{
for (int i = 0; i < 100; i++)
{
try
{
Console.WriteLine($"Producing something: {i}");
MightThrowExceptionForProducer(i);
await Task.Delay(10);
TotalProduced++;
await writer.WriteAsync(i);
}
catch (Exception ex)
{
Console.WriteLine($"Logged: {ex.Message}");
}
}
writer.Complete();
}</code></pre>
<div><br /></div>
<div>
Inside the "for" loop, we have a try/catch block. So if an exception is
thrown, it is caught and logged, and the loop processing will continue.
</div>
<div><br /></div>
<div>
Marking the ChannelWriter "Complete" no longer needs to be in a "finally"
block. We will not have any unhandled exceptions escape here (and if we did
get one, it would probably be an unrecoverable one).
</div>
<div><br /></div>
<div>
<div>
One other new item is "TotalProduced++". This is a static variable that
is updated with the total number of items successfully produced (it is after
the "MightThrow..." method). This lets us track how many items were
successful in the Producer.
</div>
<div><br /></div>
</div>
<h3 style="text-align: left;">Throwing Exceptions</h3>
<div>
To make the output more interesting, the methods that might throw exceptions
use a random number generator to see if an exception should be thrown (in
the <a href="https://github.com/jeremybytes/channel-exceptions/blob/main/ChannelExceptions/doesnt-stop/Program.cs">"Program.cs" file in the "doesnt-stop" project</a>):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTFDBHE26xE38tghiG5hqq4nBLT-lm1WMa1leO7hRO4C7nEcEMwOM59EGXABt-0LsQtfOzeIxcVuiM3s0wh7Ir0W_2tZGsal_cBkvOgZLFjlf6sMfgARYFSKa9SQ1h9tDIPIRkw7vzH54O3o3LTVmbc8_EjyFv2PMQQ1swfiCGdNF9684ZU0e6iWmPxEA/s952/MightThrow.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="300" data-original-width="952" height="202" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTFDBHE26xE38tghiG5hqq4nBLT-lm1WMa1leO7hRO4C7nEcEMwOM59EGXABt-0LsQtfOzeIxcVuiM3s0wh7Ir0W_2tZGsal_cBkvOgZLFjlf6sMfgARYFSKa9SQ1h9tDIPIRkw7vzH54O3o3LTVmbc8_EjyFv2PMQQ1swfiCGdNF9684ZU0e6iWmPxEA/w640-h202/MightThrow.png" width="640" /></a>
</div>
<pre><code> private static void MightThrowExceptionForProducer(int item)
{
if (Randomizer.Next() % 3 == 0)
throw new Exception($"Bad thing happened in Producer ({item})");
}
private static void MightThrowExceptionForConsumer(int item)
{
if (Randomizer.Next() % 50 == 0)
throw new Exception($"Bad thing happened in Consumer ({item})");
}</code></pre>
<div>
The Producer throws an exception 1 out of 3 times (approximately). The
Consumer throws an exception 1 out of 50 times (approximately).
</div><div><br /></div>
<h2 style="text-align: left;">Program Output</h2>
<div>
So lets see how this program behaves. We only modified the Producer and
Consumer code. The original "Main" and "ProducerConsumerWithExceptions" are
unchanged. To give us more of a chance to get random exceptions, I also
changed the code so that it produces 100 items (instead of 20).
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPLgz9me5ozGROl0mBrpDOsoTi3_YX5hiDz4j7LcXLVUPQRbWqrwqS1VYXSdshjLVzRksUPUzdT5EWdcxnhsMhEk1-MlMXXojQkwK1JcWKkBE22dEO5X_ibDJKDuXtEko83pvGxFQy7Ce1FWwNhGb19Dq4x_EH160ndQlGrFls-DWDXAYjkEooKGta3Zo/s895/Output.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="876" data-original-width="895" height="626" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPLgz9me5ozGROl0mBrpDOsoTi3_YX5hiDz4j7LcXLVUPQRbWqrwqS1VYXSdshjLVzRksUPUzdT5EWdcxnhsMhEk1-MlMXXojQkwK1JcWKkBE22dEO5X_ibDJKDuXtEko83pvGxFQy7Ce1FWwNhGb19Dq4x_EH160ndQlGrFls-DWDXAYjkEooKGta3Zo/w640-h626/Output.png" width="640" /></a>
</div>
<pre><code>Consuming object: 90
Logged: Bad thing happened in Producer (91)
Producing something: 92
Producing something: 93
Consuming object: 92
Producing something: 94
Consuming object: 93
Logged: Bad thing happened in Producer (94)
Producing something: 95
Producing something: 96
Consuming object: 95
Producing something: 97
Consuming object: 96
Producing something: 98
Consuming object: 97
Logged: Bad thing happened in Consumer (97)
Producing something: 99
Consuming object: 98
Consuming object: 99
Total Produced: 69
Total Consumed: 66
Done</code></pre>
<div>This is just the tail end of the output. It shows several logged items (2 Producer errors and 1 Consumer error).</div><div><br /></div><div>Notice that the Total Produced (69) and Total Consumed (66) do not match. This is because the Producer only produced 69 items (of the 100), so the Consumer had a chance to process at most 69 items. The Consumer also generated a few errors (3), so it is showing 3 fewer items.</div><div><br /></div><div>"Done" is printed out at the bottom. This lets us know that the application ran to completion and exited.</div><div><br /></div><h2 style="text-align: left;">A More Reliable Approach</h2><div>Our application is more reliable when the Producer and Consumer can deal with their own exceptions. This is not always possible, but it is a good approach for the sample code that we have here.</div><div><br /></div><div>But we always need to consider what our particular program is doing. I had a great question come up in a recent workshop. The developer asked, </div><div><span style="font-size: large;"><b></b></span><blockquote><span style="font-size: large;"><b>"If we get an error in the Consumer, can we just put the item back onto the Channel for it to try again?"</b></span></blockquote></div><div>If the application has random hiccups that can be solved by re-processing, then maybe this is a good solution. But there is a problem: what if it isn't a random hiccup?</div><div><br /></div><div>If the Consumer fails every time it tries to process a particular record, then putting it back on the Channel will only create an endless loop of failures.</div><div><br /></div><div>One approach that we came up with was to have a separate Channel for errors. So if the Consumer failed to process an item, it would put it on the error channel. This would be processed in a different workflow. For example, the Consumer of the error channel could try to process the item again and then log an error if it was unsuccessful. This would give the application a chance to try again (to handle the random hiccups) and also to log errors for items that could not be processed at all.</div><div><br /></div><h2 style="text-align: left;">Wrap Up</h2><div>As with most code, there are a lot of options. When I'm writing code, I usually spend a lot more time thinking than typing. Working through different scenarios lets me come up with a solution at a high level and figure out how all of the pieces fit together. Typing the code is the easy part.</div><div><br /></div><div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-29437954790862066482023-10-05T20:00:00.348-07:002023-10-06T13:17:38.514-07:00Looking at Producer/Consumer Dependencies: Bounded vs. Unbounded Channels<div>
In the last article, "<a href="https://jeremybytes.blogspot.com/2023/10/dont-use-taskwhenall-for-interdependent.html">Don't Use 'Task.WhenAll' for Interdependent Tasks</a>", we saw how using WhenAll for tasks that are interdependent can cause an
application to hang if one of the tasks throws an exception. As a solution, we
changed from using "Task.WhenAll" to awaiting the tasks separately. But there
are other approaches.
</div>
<div><br /></div>
<div>
In this article, we will focus on breaking the dependency between the tasks.
In the sample code, the channel is bounded (meaning, it has a maximum
capacity). This can create a dependency between the Producer and Consumer. One
way to break the dependency is to use an unbounded channel.
</div>
<div><br /></div>
<div></div>
<blockquote>
<div>
<span style="font-size: large;"><b>A Bounded Channel can create a dependency between a Producer and
Consumer. Using an Unbounded Channel is one way to break that
dependency.</b></span>
</div>
<div></div>
</blockquote>
<div><br /></div>
<div>
Let's do a quick review of the code that we saw yesterday and then look at how
changing the channel can affect the code.
</div>
<div><br /></div>
<h2 style="text-align: left;">Motivation</h2>
<div>
This article is based on a question sent in by Edington Watt regarding a presentation
that I did about
<a href="https://github.com/jeremybytes/csharp-channels-presentation">Channel<T> in C#</a>. He and the WTW ICT Technology team wrote a sample project that showed
strange behavior when exceptions are thrown, and he asked the question "What
are the best exception handling practices in the Consumer Producer approach
using Channel<T>?"
</div>
<div><br /></div>
<div>
One reason for the behavior was the way that a bounded channel created a
dependency between the Producer and Consumer. If we can break that dependency,
the behavior will change.
</div>
<div><br /></div>
<div>
<i>Note: The full source code (including the original sample code and various
approaches to fixing the strange behavior is available here: <a href="https://github.com/jeremybytes/channel-exceptions">https://github.com/jeremybytes/channel-exceptions</a>.</i>
</div>
<div><br /></div>
<div><b>Articles</b></div>
<div>
<ul style="text-align: left;">
<li><a href="https://jeremybytes.blogspot.com/2023/10/dont-use-taskwhenall-for-interdependent.html">Don't Use "Task.WhenAll" for Interdependent Tasks</a></li>
<li>
Looking at Producer/Consumer Dependencies: Bounded vs. Unbounded Channels <i>(this one)</i></li>
<li><a href="https://jeremybytes.blogspot.com/2023/10/producerconsumer-exception-handling.html">Producer/Consumer Exception Handling - A More Reliable Approach</a></li>
</ul>
</div>
<div><br /></div>
<h2 style="text-align: left;">Interdependent Tasks</h2>
<div>
We'll go back to the starting place we used in the previous article. Here are
the relevant methods (available in the
<a href="https://github.com/jeremybytes/channel-exceptions/blob/main/ChannelExceptions/refactored/Program.cs">Program.cs file of the "refactored" project</a>):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik9EbU6x3hDnqCBOeJw5q2CN8OIILEqPxTGu-ycX8nnjKE6OI7r6Y8qWSeLJlW7p9LqqNP2lYTxx_DKH8AGZXSWZeOeQAYVsuwnWPngMU38Dq-8avwiXqnqyyH1G5kanmRTbP53lWfWjMnHc6zbZclWVKpZoovdEoyEDkTWyYxgP-SXTzDLpYvzJZZ280/s872/Starting_Code.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="642" data-original-width="872" height="472" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik9EbU6x3hDnqCBOeJw5q2CN8OIILEqPxTGu-ycX8nnjKE6OI7r6Y8qWSeLJlW7p9LqqNP2lYTxx_DKH8AGZXSWZeOeQAYVsuwnWPngMU38Dq-8avwiXqnqyyH1G5kanmRTbP53lWfWjMnHc6zbZclWVKpZoovdEoyEDkTWyYxgP-SXTzDLpYvzJZZ280/w640-h472/Starting_Code.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> static async Task Main(string[] args)
{
try
{
await ProducerConsumerWithExceptions();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Done");
}
static async Task ProducerConsumerWithExceptions()
{
var channel = Channel.CreateBounded<int>(new BoundedChannelOptions(10));
Task producer = Producer(channel.Writer);
Task consumer = Consumer(channel.Reader);
await Task.WhenAll(producer, consumer);
}</code></pre>
<div><br /></div>
<div>
I won't go through this code again here. Check the
<a href="https://jeremybytes.blogspot.com/2023/10/dont-use-taskwhenall-for-interdependent.html">previous article for a walkthrough</a>. This code works fine for the success state, but it has strange behavior if
the Consumer throws an exception.
</div>
<div><br /></div>
<h3 style="text-align: left;">Consumer Exception</h3>
<div>
If the Consumer throws an exception while processing an item, the entire
application hangs:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg67S_4u0fjERre-oDk73RnDE4_QPEuYyBkrx03SIdooibmdBDJDuYxC21-45mvdrHD9Ro8mKXehqivS9bN-GxrieJF8Dihp7kBApYHOCaKkJb2ZW6NjE075MILMvpILa_U0y0L412huU0BdJDi3oFseTTPxfSAdOeCy415_xc6ktvU8Dz8GX6xDip4Dj8/s577/Original-ErrorOutput.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="577" data-original-width="481" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg67S_4u0fjERre-oDk73RnDE4_QPEuYyBkrx03SIdooibmdBDJDuYxC21-45mvdrHD9Ro8mKXehqivS9bN-GxrieJF8Dihp7kBApYHOCaKkJb2ZW6NjE075MILMvpILa_U0y0L412huU0BdJDi3oFseTTPxfSAdOeCy415_xc6ktvU8Dz8GX6xDip4Dj8/w334-h400/Original-ErrorOutput.png" width="334" /></a>
</div>
<br />
<pre><code>Producing something: 0
Producing something: 1
Consuming object: 0
Producing something: 2
Producing something: 3
Producing something: 4
Producing something: 5
Producing something: 6
Producing something: 7
Producing something: 8
Producing something: 9
Producing something: 10
Producing something: 11
</code></pre>
<div><br /></div>
<div>Here's a reminder of what happens:</div>
<div><br /></div>
<div>
In the "ProducerConsumerWithExceptions" method, we create a Bounded Channel.
This means that the capacity of the channel is limited to 10 items. When the
channel has reached that capacity, no new items can be written to it until
space is made available for them.
</div>
<div><br /></div>
<div>
The Consumer reads one item off of the channel (index 0) and then throws an
exception. The Consumer then stops, and no further items are read off of the
channel.
</div>
<div><br /></div>
<div>
The Producer produces data and puts it onto the channel. The first items
produced (index 0) is pulled off of the channel by the Consumer. The Producer
keeps going and puts 10 more items onto the channel (indexes 1 through 10).
</div>
<div><br /></div>
<div>
Then the Producer produces index 11 and tries to put it onto the channel. At
this point, the channel is at capacity (it has 10 items already), so the
Producer waits (in an async-friendly way) for there to be space to write the
next item.
</div>
<div><br /></div>
<div>
And this is where the application hangs. The Producer will wait forever. Since
"Task.WhenAll" is waiting for the Producer and Consumer to both finish, it
will also wait forever. The application will not exit on its own.
</div>
<div><br /></div>
<h2 style="text-align: left;">Using an Unbounded Channel</h2>
<div>
What I would like to do is remove the dependency between the Producer and
Consumer. Even if the Consumer breaks, the Producer should keep going. One way
of doing this is to use an Unbounded Channel (meaning, a channel with no
capacity limit).
</div>
<div><br /></div>
<div>
This is a small change to this application in the
ProducerConsumerWithExceptions method. (This code is in the
<a href="https://github.com/jeremybytes/channel-exceptions/blob/main/ChannelExceptions/unbounded-channel/Program.cs">Program.cs file of the "unbounded-channel" project</a>):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibcdjdS-8FXv7r_JpeHoQPCQuXRHTKqzFjm6g3ohZZPwDTbZc3w7M6i4Mp4JuUcRZ7U1SuPD_6YMFlS4_w7iK8wjCqbqC0NMLXYB75O1AJn4L2VT4woW4tH0t9qYTdvsOUvQ22WpHG6vE89cEW18KOALV1u_9P9SiYGmuEpPCc2l5QV6Dt2eSq4B9LGII/s671/Unbounded_Channel.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="261" data-original-width="671" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibcdjdS-8FXv7r_JpeHoQPCQuXRHTKqzFjm6g3ohZZPwDTbZc3w7M6i4Mp4JuUcRZ7U1SuPD_6YMFlS4_w7iK8wjCqbqC0NMLXYB75O1AJn4L2VT4woW4tH0t9qYTdvsOUvQ22WpHG6vE89cEW18KOALV1u_9P9SiYGmuEpPCc2l5QV6Dt2eSq4B9LGII/w640-h248/Unbounded_Channel.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> static async Task ProducerConsumerWithExceptions()
{
var channel = Channel.CreateUnbounded<int>();
Task producer = Producer(channel.Writer);
Task consumer = Consumer(channel.Reader);
await Task.WhenAll(producer, consumer);
}</code></pre>
<div><br /></div>
<div>Now the Channel is no longer limited to 10 items.</div>
<div><br /></div>
<h3 style="text-align: left;">Error Output</h3>
<div>Let's take a look at the error output now:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj54Q_Rfgh4twth8jMN-9QR8sWpK0lLTSaTS49VsYi78N7RlUAy4LrLaMPbaR8orzO8DHrDAdD_UheVKT5PNYtDtzqDVj6TKJ4-eT16-y_2dCRmAUAf6mQb_nr3uQT5Lp28DM5F8mOaRqpUVCskWkhdR_vRweIi24-Rodv2reqErKQulS5RMqfrO3MQSHs/s926/Unbounded_Error.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="926" data-original-width="702" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj54Q_Rfgh4twth8jMN-9QR8sWpK0lLTSaTS49VsYi78N7RlUAy4LrLaMPbaR8orzO8DHrDAdD_UheVKT5PNYtDtzqDVj6TKJ4-eT16-y_2dCRmAUAf6mQb_nr3uQT5Lp28DM5F8mOaRqpUVCskWkhdR_vRweIi24-Rodv2reqErKQulS5RMqfrO3MQSHs/w486-h640/Unbounded_Error.png" width="486" /></a>
</div>
<div><br /></div>
<pre><code>Producing something: 0
Producing something: 1
Consuming object: 0
Producing something: 2
Producing something: 3
Producing something: 4
Producing something: 5
Producing something: 6
Producing something: 7
Producing something: 8
Producing something: 9
Producing something: 10
Producing something: 11
Producing something: 12
Producing something: 13
Producing something: 14
Producing something: 15
Producing something: 16
Producing something: 17
Producing something: 18
Producing something: 19
Bad thing happened in Consumer (0)
Done</code></pre>
<div><br /></div>
<div>
The most important part of this output is that we have the "<b>Done</b>"
message. This tells us that the application finished on its own. We also see
the error message "Bad thing happened in Consumer (0)" just before the "Done".
</div>
<div><br /></div>
<div>Here's how things work with this code.</div>
<div><br /></div>
<div>
<div>
In the "ProducerConsumerWithExceptions" method, we create a Unbounded
Channel. This means that the capacity of the channel is not limited.
</div>
<div><br /></div>
<div>
The Consumer reads one item off of the channel (index 0) and then throws an
exception. The Consumer then stops, and no further items are read off of the
channel.
</div>
<div><br /></div>
<div>
The Producer produces data and puts it onto the channel. The first items
produced (index 0) is pulled off of the channel by the Consumer. The
Producer keeps going and puts 19 more items onto the channel (indexes 1
through 19). Once it has produced all 20 items, the Producer completes.
</div>
<div><br /></div>
<div>
The Producer Task has completed (successfully) and the Consumer Task has
completed (with error). This means that "await Task.WhenAll" can move
foward.
</div>
</div>
<div><br /></div>
<div>
Since one of the tasks got an exception, the "await" will raise that exception
here. But "ProducerConsumerWithException" does not handle the exception, so it
bubbles up to the "Main" method.
</div>
<div><br /></div>
<div>
The "Main" method has a try/catch block. It catches the thrown exception,
prints the error message to the console, outputs "Done", and then the
application exits.
</div>
<div><br /></div>
<h2 style="text-align: left;">Bounded vs. Unbounded Channels</h2>
<div>
What we've seen here is that using a Bounded Channel can create a dependency
between a Producer and Consumer. If the Consumer fails and the Channel reaches
capacity, the Producer will never complete.
</div>
<div><br /></div>
<div>With an Unbounded Channel, we no longer have that same dependency.</div>
<div><br /></div>
<div>
I say that a Bounded Channel "<b>can</b>" create a dependency because it
doesn't necessarily create a dependency. There are different ways to write to
a Channel that would not create a dependency.
</div>
<div><br /></div>
<h3 style="text-align: left;">Writing to a Channel</h3>
<div>
There are multiple ways of writing to a channel. In this code, the Producer
uses "WriteAsync":
</div>
<pre><code> await writer.WriteAsync(i);</code></pre>
<div><br /></div>
<div>
"WriteAsync" is a method on the ChannelWriter<T> class. If the channel
is at capacity, it will wait until there is space available. This is the
easiest way to write to a channel, so I tend to use this most of the time. It
does have the downside of possibly waiting forever if space is never made
available.
</div>
<div><br /></div>
<div>
But there are other approaches. The ChannelWriter<T> also has a
"TryWrite" method:
</div>
<pre><code> bool success = writer.TryWrite(i);</code></pre>
<div><br /></div>
<div>
"TryWrite" is not asynchronous. If it writes to the channel successfully, it
returns "true". But if the channel is at capacity and the data cannot be
written, it returns "false".
</div>
<div><br /></div>
<div>
This has the advantage of not waiting forever. The disadvantage is that we
need a little more code to handle the workflow. What happens if "TryWrite"
returns "false"? Do we wait a bit and retry? Do we pause our Producer? Do we
simply discard that item? Do we log the item and go to the next one? It
depends on what our application needs to do. But the flexibility is there for
us to decide.
</div>
<div><br /></div>
<div>
To help us with the workflow, there is also a "WaitToWriteAsync" method:
</div>
<pre><code> await writer.WaitToWriteAsync();</code></pre>
<div><br /></div>
<div>
If the channel is at capacity, this will wait (in an async-friendly way) for
space to be available. We can use this to be pretty sure that "TryWrite" will
succeed. (But with parallel code, it might not always be the case.)
</div>
<div><br /></div>
<div>
When we await "WaitToWriteAsync", however, we are back to a situation where we
may end up waiting forever if the channel is at capacity and no new items are
ready off of it.
</div>
<div><br /></div>
<h3 style="text-align: left;">Cancellation Tokens</h3>
<div>
Another part of the async methods on ChannelWriter<T> is that they take
optional cancellation tokens.
</div>
<pre><code> await writer.WriteAsync(i, cancellationToken);</code></pre>
<div><br /></div>
<div>
We could be pretty creative with a cancellation token. For example, if the
Consumer throws an exception, it could also set the cancellation token to the
"cancellation requested" state. Then "WriteAsync" would stop waiting.
</div>
<div><br /></div>
<div>
The same is true if we use a cancellation token with "WaitToWriteAsync".
</div>
<div><br /></div>
<h3 style="text-align: left;">A Lot of Options</h3>
<div>
As with many things in C#, Channel<T> has a lot of options. This can be
good because of the flexibility that we have as programmers. If we need to do
something a little unusual, there are often methods and properties that help
us out. The downside is that there is a bit more to learn, and it can be
confusing to figure out which approach is the best one for your application.
</div>
<div><br /></div>
<h2 style="text-align: left;">More Solutions</h2>
<div>
So we've seen a couple of solutions to fix the weird behavior of the sample
application when exceptions are thrown.
</div>
<div><br /></div>
<div>
If we await the Producer and Consumer tasks separately, we do not need to be
as concerned about them being dependent on one another.
</div>
<div><br /></div>
<div>
If we change the Channel from Bounded to Unbounded, we remove the dependency
between the Producer and Consumer.
</div>
<div><br /></div>
<div>
If we add some exception handling inside the Producer and Consumer Tasks, we
can continue processing data even if there are errors. (This approach is shown
in the "doesnt-stop" project in the GitHub repository: <a href="https://github.com/jeremybytes/channel-exceptions">https://github.com/jeremybytes/channel-exceptions</a>.) We'll look at this in a future article.
</div>
<div><br /></div>
<h2 style="text-align: left;">Wrap Up</h2>
<div>
<div>
As we dive deeper into different ways of using asynchronous and parallel
bits in our code, we often find strange behavior. And sometimes it can take
a while to figure out where the strangeness is coming from. But the more
that we learn, the easier it gets. So keep learning and keep writing amazing
applications that make your users happy.
</div>
<div><br /></div>
<div>Happy Coding!</div>
</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-62568683868077943802023-10-04T08:00:00.557-07:002023-10-06T13:17:51.394-07:00Don't Use "Task.WhenAll" for Interdependent Tasks<div>
Using "Task.WhenAll" gives us a chance to pause our code until multiple tasks
are complete. In addition, if there is an unhandled exception in one of those
tasks, awaiting the WhenAll will raise that thrown exception so we can deal
with it. This all works fine if our tasks are not dependent on each other. But
if the tasks are interdependent, this can cause the application to hang.
</div>
<div><br /></div>
<div><b>Short Version:</b> </div>
<blockquote>
<span style="font-size: large;"><b>
"Task.WhenAll" works great for independent tasks. If the tasks are
dependent on one another, it can be better to await the individual tasks
instead.
</b></span>
</blockquote>
<div>
When we start using tasks and parallel code in our applications, we can run
into some weirdness that can be difficult to debug. Today we'll look at some
of the weirdness that can occur around "Task.WhenAll" and understand how to
alleviate it.
</div>
<div><br /></div>
<h2 style="text-align: left;">Motivation</h2>
<div>
This article is based on a question sent in by Edington Watt regarding a
presentation that I did about
<a href="https://github.com/jeremybytes/csharp-channels-presentation">Channel<T> in C#</a>. He and the WTW ICT Technology team wrote a sample project that showed
strange behavior when exceptions are thrown, and he asked the question "What
are the best exception handling practices in the Consumer Producer approach
using Channel<T>?"
</div>
<div><br /></div>
<div>
One reason for the behavior was the way that "await Task.WhenAll" was used in
the code. We will see some of that code and a solution to it after a bit of an
overview of how I generally use "Task.WhenAll".
</div>
<div><br /></div>
<div>
<i>Note: The full source code (including the original sample code and various
approaches to fixing the strange behavior is available here: <a href="https://github.com/jeremybytes/channel-exceptions">https://github.com/jeremybytes/channel-exceptions</a>.</i>
</div>
<div><br /></div>
<div><b>Articles</b></div>
<div>
<ul style="text-align: left;">
<li>Don't Use "Task.WhenAll" for Interdependent Tasks <i>(this one)</i></li>
<li><a href="https://jeremybytes.blogspot.com/2023/10/looking-at-producerconsumer.html">Looking at Producer/Consumer Dependencies: Bounded vs. Unbounded Channels</a></li>
<li><a href="https://jeremybytes.blogspot.com/2023/10/producerconsumer-exception-handling.html">Producer/Consumer Exception Handling - A More Reliable Approach</a></li>
</ul>
</div>
<div><br /></div>
<h2 style="text-align: left;">Awaiting Non-Dependent Tasks</h2>
<div>
I use "Task.WhenAll" in a couple of scenarios. The first is when I want to run
several of the same tasks in parallel, and I need to wait for all of them to
complete.
</div>
<div><br /></div>
<h3 style="text-align: left;">Parallel Tasks</h3>
<div>
Here is some code from my workshop on "Asynchronous and Parallel Programming
in C#". (<i>Alert: The last scheduled public workshop on this topic is coming up
soon: <a href="https://jeremybytes.blogspot.com/2023/09/last-chance-full-day-workshop-on.html">https://jeremybytes.blogspot.com/2023/09/last-chance-full-day-workshop-on.html</a>.</i>)
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWn77tg0Qk0kzK5HP3-iu2QzySze5NlEsQJPL14MUwar3jzNMpf5txPwEjC22JWzxQX1yXG2K1ucGmSjP7XcRYKTuEgItyZZ4mqdk-QL8YkyQXQAmP2B22cmvhKQn3DPmZMcTKrlsjmK5T1bHN7u1mXvAVumBdUz5GhfdcIl0fCpfFZPnXvx0P3OfsyfI/s1236/WhenAll-Independent.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="487" data-original-width="1236" height="252" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiWn77tg0Qk0kzK5HP3-iu2QzySze5NlEsQJPL14MUwar3jzNMpf5txPwEjC22JWzxQX1yXG2K1ucGmSjP7XcRYKTuEgItyZZ4mqdk-QL8YkyQXQAmP2B22cmvhKQn3DPmZMcTKrlsjmK5T1bHN7u1mXvAVumBdUz5GhfdcIl0fCpfFZPnXvx0P3OfsyfI/w640-h252/WhenAll-Independent.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> static async Task RunWithContinuation(List<int> ids)
{
List<Task> allContinuations = new();
foreach (var id in ids)
{
Task<Person> personTask = reader.GetPersonAsync(id);
Task continuation = personTask.ContinueWith(task => ...);
allContinuations.Add(continuation);
}
await Task.WhenAll(allContinuations);
}</code></pre>
<div><br /></div>
<div>
Inside the "foreach" loop, this code runs a method (GetPersonAsync) that
returns a Task, and then sets up a continuation to run when that first task is
complete. We do not "await" the tasks inside the loop because we want to run
them in parallel.
</div>
<div><br /></div>
<div>
Each continuation also returns a Task (which is named "continuation"). The
trick to this method is that even though I want all of these tasks to run in
parallel, I do not want to exit the method until they are all complete.
</div>
<div><br /></div>
<div>This is where "Task.WhenAll" comes in.</div>
<div><br /></div>
<div>
The continuation tasks are collected in a list (called "allContinuations").
Then I pass that collection to "Task.WhenAll". When I "await" Task.WhenAll,
the code will pause (in a nice async-friendly way) until all of the
continuation tasks are complete. This means that the method
("RunWithContinuation") will not exit until all of the tasks have completed.
</div>
<div><br /></div>
<div>
These tasks are <b>not</b> dependent on each other. Each is its own
atomic task: it gets a "Person" record based on an "id" parameter and then
uses the result of that. (The body of the continuation is hidden, but it takes
the "Person" record and outputs it for the user.)
</div>
<div><br /></div>
<div>
If any of these continuation tasks throws an exception, then that exception is
raised when we "await Task.WhenAll".
</div>
<div><br /></div>
<div>But here is one key point:</div>
<div>
<span style="font-size: large;"></span>
<blockquote>
<span style="font-size: large;">The exception is not raised until after all of the tasks have
completed.</span>
</blockquote>
</div>
<div>
And here "completed" means that it finished successfully, with an error, or by
being canceled.
</div>
<div><br /></div>
<div>
Since these tasks are not dependent on each other, they can all complete even
if one (or more) of the tasks fails.
</div>
<div><br /></div>
<h3 style="text-align: left;">Related (but not Dependent) Tasks</h3>
<div>
As another example, here is some code from a hands-on lab (from the same
workshop mentioned above). This particular lab is about dealing with
AggregateExceptions.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgK6cWe4i3Y1qw6RWRGf2RVpyuKIzb2Q9BQsZNtZI1od4vBWucJ1W88NG0s3YjAvJDoUYYtBYDRtvjyRuWYrAJC5GZvqSM9deIf5gUPinissezJ93kclvOh5UFo6jZugJ7lTvKO5NKWJ4ISjNjA0_6AxUjd5mAfRSSUP_qVGJ8NUORG4QeaZnaI_FGz_K0/s1232/WhenAll-Related.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="52" data-original-width="1232" height="28" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgK6cWe4i3Y1qw6RWRGf2RVpyuKIzb2Q9BQsZNtZI1od4vBWucJ1W88NG0s3YjAvJDoUYYtBYDRtvjyRuWYrAJC5GZvqSM9deIf5gUPinissezJ93kclvOh5UFo6jZugJ7lTvKO5NKWJ4ISjNjA0_6AxUjd5mAfRSSUP_qVGJ8NUORG4QeaZnaI_FGz_K0/w640-h28/WhenAll-Related.png" width="640" /></a>
</div>
<br />
<pre><code> await Task.WhenAll(orderDetailsTask, customerTask, productTask);</code></pre>
<div><br /></div>
<div>
In this code, we have 3 separate tasks: one to get order details, one to get a
customer, and one to get a list of products. These 3 pieces are used to
assemble a complete "Order". (Please don't write to me about how this isn't a
good way to assemble a complex object; it is code specifically to allow for
digging into AggregateException.)
</div>
<div><br /></div>
<div>
Similar to above, the "await" will cause this code to pause until all 3 of the
tasks have completed. If there is an exception in any of the tasks, it will be
raised here. As noted above, the exception will only be raised after all
3 tasks are complete.
</div>
<div><br /></div>
<div>
These tasks are related to each other, but they are not dependent on each
other. So one task failing will not stop the other tasks from completing.
</div>
<div><br /></div>
<h2 style="text-align: left;">Interdependent Tasks</h2>
<div>
Now that we have a bit of an introduction to how "Task.WhenAll" behaves, let's
look at a situation where the tasks <b>are</b> dependent on each other.
As we'll see, this can cause us some problems.
</div>
<div><br /></div>
<div>
<i>I refactored the code originally provided by Edington Watt and the WTW
ICT Technology team to show different behaviors in the code. You can get the
original code (and the various solutions) here: <a href="https://github.com/jeremybytes/channel-exceptions">https://github.com/jeremybytes/channel-exceptions</a>.</i>
</div>
<div><br /></div>
<h3 style="text-align: left;">Starting Code</h3>
<div>
We'll start at the root of the application. (The code can be found in the
<a href="https://github.com/jeremybytes/channel-exceptions/blob/main/ChannelExceptions/refactored/Program.cs">"Program.cs" file of the "refactored" project</a>.)
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikPu2Zo0Z-RF-ipLs_XCWA86sVAfg3a2DWWNdDsKMBc7hLQUCOx5WDP4bzoJxWMcwPSPeGwJ7LcUiF9tas-XuoRXA1gsuvvsE87twFGhblG5FHyb9SXju-7KWj1CiwUU580tU5IfN6jQV7nvW0U7_609i_dJa9xm6GRzIx1Xp-BqUwVqin14R_hB_fkHg/s907/Original-Main.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="467" data-original-width="907" height="330" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikPu2Zo0Z-RF-ipLs_XCWA86sVAfg3a2DWWNdDsKMBc7hLQUCOx5WDP4bzoJxWMcwPSPeGwJ7LcUiF9tas-XuoRXA1gsuvvsE87twFGhblG5FHyb9SXju-7KWj1CiwUU580tU5IfN6jQV7nvW0U7_609i_dJa9xm6GRzIx1Xp-BqUwVqin14R_hB_fkHg/w640-h330/Original-Main.png" width="640" /></a>
</div>
<br />
<pre><code> static async Task Main(string[] args)
{
try
{
await ProducerConsumerWithExceptions();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.WriteLine("Done");
}</code></pre>
<div><br /></div>
<div>
When we "await" a Task that throws an exception, that exception is raised in
our code. (If we do not await the Task, then we have to go looking for the
exception in the Task properties.)
</div>
<div><br /></div>
<div>
The idea behind this code is that if an exception is thrown in the main
"ProducerConsumerWithExceptions" method (that returns a Task), we can catch it
and output a message to the console.
</div>
<div><br /></div>
<div>
Before looking at the behavior, lets see what "ProducerConsumerWithExceptions"
does.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikwWN2Olr8hVKn_wcNUqP8yvnuN0iVIiNKnvS2jCDRzsKgtes5R1usxb6SY_Ck1Mbm96vnyfyNGVV6Rml14u5wQ0tB0aynuOMP5XGHzgI9e1VsGMM5Ba_R1IsthAjHP-iHTqCnyoGVxpG8R65jeW0Ae9sK5-zpcc5IGTtu7EhvrxW7usAES8HnfACLJ-w/s1265/Original-Produce.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="376" data-original-width="1265" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikwWN2Olr8hVKn_wcNUqP8yvnuN0iVIiNKnvS2jCDRzsKgtes5R1usxb6SY_Ck1Mbm96vnyfyNGVV6Rml14u5wQ0tB0aynuOMP5XGHzgI9e1VsGMM5Ba_R1IsthAjHP-iHTqCnyoGVxpG8R65jeW0Ae9sK5-zpcc5IGTtu7EhvrxW7usAES8HnfACLJ-w/w640-h190/Original-Produce.png" width="640" /></a>
</div>
<br />
<pre><code> static async Task ProducerConsumerWithExceptions()
{
var channel = Channel.CreateBounded<int>(new BoundedChannelOptions(10));
Task producer = Producer(channel.Writer);
Task consumer = Consumer(channel.Reader);
await Task.WhenAll(producer, consumer);
}</code></pre>
<div><br /></div>
<div>
This code uses the Producer/Consumer pattern with a Channel in the middle. The
idea is that the Producer can produce data in parallel and then put the data
onto the Channel. The Consumer reads data off of the Channel and does
something with it (in this case, it outputs it to the console).
</div>
<div><br /></div>
<div>
This code has an "await Task.WhenAll". So this method will not exit until both
of these tasks (the produder and the consumer) have completed. And if either
of the tasks throws an exception, it will be raised here. Since we are not
handling the exception in this method, it will bubble up and be handled in the
"Main" method above.
</div>
<div><br /></div>
<div><b>Success State</b></div>
<div>
If both the producer and consumer complete successfully, we get output similar
to the following:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpKbQfOuCGE2MzkIAdSUzwDYuQrEd_lz1s_HgR5WByv05e97vlsVYzQePOeF6Wn05A7MmZTqGl4LtiZtqHMH3t5KS8xOaJRJmep8AZhbLHugtgTl-DPlRNPx87Lup95bKWL8e7g7GwBmga9gyYAOTv5ugbiTd2HNwpUoWBL7acrpKhMMcjT0tDmgd0JIY/s728/Original-NoErrorOutput.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="728" data-original-width="501" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgpKbQfOuCGE2MzkIAdSUzwDYuQrEd_lz1s_HgR5WByv05e97vlsVYzQePOeF6Wn05A7MmZTqGl4LtiZtqHMH3t5KS8xOaJRJmep8AZhbLHugtgTl-DPlRNPx87Lup95bKWL8e7g7GwBmga9gyYAOTv5ugbiTd2HNwpUoWBL7acrpKhMMcjT0tDmgd0JIY/w275-h400/Original-NoErrorOutput.png" width="275" /></a>
</div>
<br />
<pre><code>...
Producing something: 12
Consuming object: 11
Producing something: 13
Consuming object: 12
Producing something: 14
Consuming object: 13
Producing something: 15
Consuming object: 14
Producing something: 16
Consuming object: 15
Producing something: 17
Consuming object: 16
Producing something: 18
Consuming object: 17
Producing something: 19
Consuming object: 18
Consuming object: 19
Done</code></pre>
<div>
This is is just showing the end part of the output, but we can see that the
producer is producing objects and the consumer is consuming objects. Once 20
items have been produced (starting with index 0), the application prints
"Done" and exits.
</div>
<div><br /></div>
<div><b>Error Behavior</b></div>
<div>
Things get strange when we get an exception. Here is the output when the
Consumer throws an exception on reading the first record (index 0):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg67S_4u0fjERre-oDk73RnDE4_QPEuYyBkrx03SIdooibmdBDJDuYxC21-45mvdrHD9Ro8mKXehqivS9bN-GxrieJF8Dihp7kBApYHOCaKkJb2ZW6NjE075MILMvpILa_U0y0L412huU0BdJDi3oFseTTPxfSAdOeCy415_xc6ktvU8Dz8GX6xDip4Dj8/s577/Original-ErrorOutput.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="577" data-original-width="481" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg67S_4u0fjERre-oDk73RnDE4_QPEuYyBkrx03SIdooibmdBDJDuYxC21-45mvdrHD9Ro8mKXehqivS9bN-GxrieJF8Dihp7kBApYHOCaKkJb2ZW6NjE075MILMvpILa_U0y0L412huU0BdJDi3oFseTTPxfSAdOeCy415_xc6ktvU8Dz8GX6xDip4Dj8/w334-h400/Original-ErrorOutput.png" width="334" /></a>
</div>
<br />
<pre><code>Producing something: 0
Producing something: 1
Consuming object: 0
Producing something: 2
Producing something: 3
Producing something: 4
Producing something: 5
Producing something: 6
Producing something: 7
Producing something: 8
Producing something: 9
Producing something: 10
Producing something: 11
</code></pre>
<div><br /></div>
<div>
From the output, the Producer produced 12 items (and then stopped). The
Consumer throws an exception while reading the first record, so we only see
output for 1 item here.
</div>
<div><br /></div>
<div>
At first it seems strange that we do not see the exception in the output;
after all, that is part of the try/catch block in the Main method.
</div>
<div><br /></div>
<div>But here's the real problem: <b>The application is still running!</b></div>
<div><br /></div>
<div><b>Interdependent Tasks</b></div>
<div>The application is stuck on awaiting Task.WhenAll.</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikwWN2Olr8hVKn_wcNUqP8yvnuN0iVIiNKnvS2jCDRzsKgtes5R1usxb6SY_Ck1Mbm96vnyfyNGVV6Rml14u5wQ0tB0aynuOMP5XGHzgI9e1VsGMM5Ba_R1IsthAjHP-iHTqCnyoGVxpG8R65jeW0Ae9sK5-zpcc5IGTtu7EhvrxW7usAES8HnfACLJ-w/s1265/Original-Produce.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="376" data-original-width="1265" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEikwWN2Olr8hVKn_wcNUqP8yvnuN0iVIiNKnvS2jCDRzsKgtes5R1usxb6SY_Ck1Mbm96vnyfyNGVV6Rml14u5wQ0tB0aynuOMP5XGHzgI9e1VsGMM5Ba_R1IsthAjHP-iHTqCnyoGVxpG8R65jeW0Ae9sK5-zpcc5IGTtu7EhvrxW7usAES8HnfACLJ-w/w640-h190/Original-Produce.png" width="640" /></a>
</div>
<br />
<pre><code> static async Task ProducerConsumerWithExceptions()
{
var channel = Channel.CreateBounded<int>(new BoundedChannelOptions(10));
Task producer = Producer(channel.Writer);
Task consumer = Consumer(channel.Reader);
await Task.WhenAll(producer, consumer);
}</code></pre>
<div><br /></div>
<div>
The code is hung because the Producer and Consumer are interdependent. The
reason for this is a bit subtle if you're not familiar with how
Channel<T> works in C#.
</div>
<div><br /></div>
<div>
On the first line of this method, we create a bounded channel. A bounded
channel is limited to holding a certain number of items. In this case, the
channel is limited to holding 10 items. Once the channel has reached capacity,
no new items can be written until one has been read off.
</div>
<div><br /></div>
<div>
So here's what is happening. The Producer starts producing data (with index 0)
and putting it onto the channel. The consumer reads the first item (index 0)
off of the channel and then throws an exception. The Consumer task is now
faulted (meaning, an exception was thrown in the Task). The Consumer tasks is
now "complete".
</div>
<div><br /></div>
<div>
But the Producer keeps working. It keeps producing records until the channel
reaches capacity (holding indexes 1 through 10). It then produces index 11 and
then pauses. The code to write to the channel is waiting (in an async-friendly
way) for there to be space in the channel. But the channel is at capacity. And
since the Consumer is faulted, no more items will be read off of the channel.
So the Producer is left waiting forever.
</div>
<div><br /></div>
<div>
Since the Producer does not complete, the "await Task.WhenAll" will also wait
forever. Because of this, the exception is never raised in this part of the
code, so the "Main" try/catch block never has a chance to handle it. The
application will never complete on its own.
</div>
<div><br /></div>
<h2 style="text-align: left;">One Solution - "await" Tasks Separately</h2>
<div>
One solution to this particular problem is to "await" the Producer and
Consumer tasks separately. Here is what that code looks like. (The code
can be found in the <a href="https://github.com/jeremybytes/channel-exceptions/blob/main/ChannelExceptions/separate-await/Program.cs">"Program.cs" file of the "separate-await" project</a>.)
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE7Ea11x5j3ySoex0n8NQpMjnpMJTBXMF4hnerQWBbtKcWeD2SHEcYEUge5-rXxJLGO7Sgcs5lBcu15lhKMrWSC23FnJKlwAiGRTzehhEdU3ZC0-pFMQMrr23CfpGoY75R1QvchUlSLmGPn-TCIjZ0mr_seYZHFvtfZQUiJwlRg11qqmrzGZhrPA1giz4/s1262/SeparateAwait-Code.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="493" data-original-width="1262" height="250" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE7Ea11x5j3ySoex0n8NQpMjnpMJTBXMF4hnerQWBbtKcWeD2SHEcYEUge5-rXxJLGO7Sgcs5lBcu15lhKMrWSC23FnJKlwAiGRTzehhEdU3ZC0-pFMQMrr23CfpGoY75R1QvchUlSLmGPn-TCIjZ0mr_seYZHFvtfZQUiJwlRg11qqmrzGZhrPA1giz4/w640-h250/SeparateAwait-Code.png" width="640" /></a>
</div>
<pre><code> static async Task ProducerConsumerWithExceptions()
{
var channel = Channel.CreateBounded<int>(new BoundedChannelOptions(10));
Task producer = Producer(channel.Writer);
Task consumer = Consumer(channel.Reader);
await consumer;
await producer;
}</code></pre>
<div><br /></div>
<div>
Notice that instead of using "await Task.WhenAll", we now have separate "await
consumer" and "await producer" sections. Since our producer is dependent upon
the consumer making space available in the channel, it is best to wait for the
consumer first here. (It's okay if the subtleties of that get lost; parallel
async code is "fun".)
</div>
<div><br /></div>
<div>Here is the output from the code above:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhacjFxL9bN8bLxQ1m1SJAbTImuuhapluQIiIxb4iqs3oNMIGK53D6JOEQ9vi_arg61-ZA1ZnEE01M4wtbXnJEgG0GcLV2rcPRO-mJqm0-cbOCeVQV91DWx9jy8Vaj4NtDbSN8NRbDvSjks7Xyo4D-f9cPBkM0hHxC551Li76pTpU8udy3mH-QOphhK3uw/s697/SeparateAwait-Output.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="355" data-original-width="697" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhacjFxL9bN8bLxQ1m1SJAbTImuuhapluQIiIxb4iqs3oNMIGK53D6JOEQ9vi_arg61-ZA1ZnEE01M4wtbXnJEgG0GcLV2rcPRO-mJqm0-cbOCeVQV91DWx9jy8Vaj4NtDbSN8NRbDvSjks7Xyo4D-f9cPBkM0hHxC551Li76pTpU8udy3mH-QOphhK3uw/w400-h204/SeparateAwait-Output.png" width="400" /></a>
</div>
<pre><code>Producing something: 0
Producing something: 1
Consuming object: 0
Producing something: 2
Producing something: 3
Bad thing happened in Consumer (0)
Producing something: 4
Done</code></pre>
<div><br /></div>
<div>
The output shows the Producer producing items (indexes 0 through 4 in this
case). The Consumer pulls the first item (index 0) from the channel and then
throws an exception.
</div>
<div><br /></div>
<div>
The line "await consumer" above waits for the Consumer to complete. As soon as
the Consumer task throws an exception, it is complete (in the "faulted"
state). And since we awaited the faulted task, the exception gets raised.
</div>
<div><br /></div>
<div>
The exception is not handled in this method, so the method short-circuits
(meaning we never get to the "await producer" line). The exception bubbles up
to the "Main" method, and that try/catch block outputs the exception message
to the console ("Bad thing happened in Consumer (0)").
</div>
<div><br /></div>
<div>
Most importantly, we see the "Done" message which means that our application
exited on its own.
</div>
<div><br /></div>
<div>
<i>Side note: We get the message "Producing something: 4" after the exception
message because the Producer is still running its code concurrently. So, the
Producer has a chance to produce one more item before the application
exits.</i>
</div>
<div><br /></div>
<h2 style="text-align: left;">Task.WhenAll and Interdependent Tasks</h2>
<div>
So we've seen that if we have interdependent tasks, awaiting Task.WhenAll can
lead our program to hang. In this situation, one task failing (the consumer)
prevented the other task from completing (the producer). Since not all tasks
complete, "await Task.WhenAll" ends up waiting forever.
</div>
<div>
<span style="font-size: large;"><b></b></span>
<blockquote>
<span style="font-size: large;"><b>If we have interdependent tasks, it is better to await the tasks
separately. If we have tasks which are not dependent (such as the
parallel and related examples above), then "await Task.WhenAll" usually
works just fine.</b></span>
</blockquote>
</div>
<div><br /></div>
<h2 style="text-align: left;">More Solutions</h2>
<div>
There are other ways to fix the strange behavior of the sample application.
You can take a look at some of the other solutions in the GitHub
repository: <a href="https://github.com/jeremybytes/channel-exceptions">https://github.com/jeremybytes/channel-exceptions</a>.
</div>
<div><br /></div>
<div>
Another solution is to change the bounded channel to an unbounded channel (in
the "unbounded-channel" project). This gets rid of the capacity limitation and
removes the dependency between the Producer task and Consumer task.
<i>[Update - article available:
<a href="https://jeremybytes.blogspot.com/2023/10/looking-at-producerconsumer.html">Looking at Producer/Consumer Dependencies: Bounded vs. Unbounded
Channels</a>]</i>
</div>
<div><br /></div>
<div>
Both this solution and the one presented in this article are short-circuiting
-- meaning, if the Consumer throws an exception on one item, no other items
will be consumed. The same is true of the Producer.
</div>
<div><br /></div>
<div>
The GitHub repository also provides a more robust implementation (in the
"doesnt-stop" project). In this project, if the Consumer throws an exception
while processing an item, it will log the error and continue processing. (The
same is true of the Producer in this project.)
</div>
<div><br /></div>
<h2 style="text-align: left;">Wrap Up</h2>
<div>
As we dive deeper into different ways of using asynchronous and parallel bits
in our code, we often find strange behavior. And sometimes it can take a while
to figure out where the strangeness is coming from. But the more that we
learn, the easier it gets. So keep learning and keep writing amazing
applications that make your users happy.
</div>
<div><br /></div>
<div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-53389476608604660762023-09-25T20:27:00.001-07:002023-09-25T20:27:07.520-07:00Last Chance: Full Day Workshop on Asynchronous and Parallel Programming in C#<p>
This is the last public workshop I have scheduled on asynchronous programming.
Next year, I've got a whole new workshop coming. So if you've been putting off
attending, you'd better take the opportunity now!
</p>
<p>
On Sunday, November 12, 2023, I'll be giving a full day workshop (with
hands-on labs) at LIVE! 360 in Orlando, Florida. This is your chance to spend
a full day with me and also learn tons of useful tech during the rest of the
week.
</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcEjWiSPOd3lpWGayOai72pQsVvGBqp3wN9n_5LikqyKLoCiNY0IPAbWYYqTDxnIjuYHXG5BM4oQQpKVxM9kmg7xdmCFNFh-NnPIuvQOvh7GFLDderCiS8jnTjddEEoV-e43a75qdutXoPJuFrIi5ed0aDvIZcOJtrP_rI6bLIFskEtVs850DIf0HR4cI/s1200/Clark_L360_OR23_Speaking_Badge.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="627" data-original-width="1200" height="334" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcEjWiSPOd3lpWGayOai72pQsVvGBqp3wN9n_5LikqyKLoCiNY0IPAbWYYqTDxnIjuYHXG5BM4oQQpKVxM9kmg7xdmCFNFh-NnPIuvQOvh7GFLDderCiS8jnTjddEEoV-e43a75qdutXoPJuFrIi5ed0aDvIZcOJtrP_rI6bLIFskEtVs850DIf0HR4cI/w640-h334/Clark_L360_OR23_Speaking_Badge.jpg" width="640" /></a></div>
<h3 style="text-align: left;">Event Info:</h3>
<blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;">
LIVE! 360 November 12-17, 2023<br />
Royal Pacific Resort at Universal<br />
Orlando, FL<br />
Event Link: <a href="https://live360events.com/Events/Orlando-2023/Home.aspx">https://live360events.com/Events/Orlando-2023/Home.aspx</a>
<br />
</blockquote>
<p></p>
<blockquote>
<span style="font-size: large;"><b>Use the promo code "Clark" to save $500 off the regular price for 4-, 5-,
or 6-day packages (Note: you'll need the 6-day package to join me for the
full day workshop on Sunday, November 12). Here's a direct link to
registration that includes the promo code: <a href="http://bit.ly/3LuBLrd">bit.ly/3LuBLrd</a></b></span>
</blockquote>
<a href="http://bit.ly/3HSDHJn"></a>
<p></p>
<p>Read on to see what we'll learn in the workshop.</p>
<h2 style="text-align: left;">
Hands-on Lab: Asynchronous and Parallel Programming in C#
</h2>
<p>11/12/2023 9:00 a.m. - 6:00 p.m.<br />Level: <i>Intermediate</i></p>
<p>
Asynchronous programming is a critical skill to take full advantage of today's
multi-core systems. But async programming brings its own set of issues. In
this workshop, we'll work through some of those issues and get comfortable
using various parts of the .NET Task Parallel Library (TPL).
</p>
<p>
We'll start by calling asynchronous methods using the Task Asynchronous
Pattern (TAP), including how to handle exceptions and cancellation. With this
in hand, we'll look at creating our own asynchronous methods and methods that
use asynchronous libraries. Along the way, we'll see how to avoid deadlocks,
how to isolate our code for easier async, and why it's important to stay away
from "asyc void".
</p>
<p>
In addition, we'll look at some patterns for running code in parallel,
including using Parallel.ForEachAsync, channels, and other techniques. We'll
see pros and cons so that we can pick the right pattern for a particular
problem.
</p>
<p>
Throughout the day, we'll go hands-on with lab exercises to put these skills
into practice.
</p>
<h3 style="text-align: left;">Objectives:</h3>
<p></p>
<ul style="text-align: left;">
<li>Use asynchronous methods with Task and await </li>
<li>Create asynchronous methods and libraries </li>
<li>Learn to avoid deadlocks and other pitfalls </li>
<li>Understand different parallel programming techniques</li>
</ul>
<h3 style="text-align: left;">Topics:</h3>
<p>Here's a list of some of the topics that we'll cover:</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_Vi4oAk6ss6nJtv1D6rTEPgzVQuBpxda469RaiMzp2_OG6gjpNCLsbh6G4vy8JlkmI85upNspeT61KK0ExP2jABA8YAcFJP_Gv6O6_IfY-hGDotGl4hWpKtwNA-xHbAbiFsbpQIcYEWyQCU23iF9yn6iuopRduFZzW2vIEZeFeJDhyj6ebIDo3d3Z_A4/s1280/topic_slide.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="720" data-original-width="1280" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_Vi4oAk6ss6nJtv1D6rTEPgzVQuBpxda469RaiMzp2_OG6gjpNCLsbh6G4vy8JlkmI85upNspeT61KK0ExP2jABA8YAcFJP_Gv6O6_IfY-hGDotGl4hWpKtwNA-xHbAbiFsbpQIcYEWyQCU23iF9yn6iuopRduFZzW2vIEZeFeJDhyj6ebIDo3d3Z_A4/w640-h360/topic_slide.png" width="640" /></a></div>
<p></p>
<h3 style="text-align: left;">Pre-Requisites:</h3>
<p>
Basic understanding of C# and object-oriented programming (classes,
inheritance, methods, and properties). No prior experience with asynchronous
programming is necessary; we'll take care of that as we go.
</p>
<h3 style="text-align: left;">Attendee Requirements:</h3>
<ul style="text-align: left;">
<li>
You must provide your own laptop computer (Windows, Mac, or Linux) for this
hands-on lab.
</li>
<li>
All other laptop requirements will be provided to attendees 2 weeks prior to
the conference
</li>
</ul>
<h3 style="text-align: left;">Hope to See You There!</h3>
<p>
This is the last scheduled public asynchronous programming workshop. If you
can't make it to this one, I am available for private workshops for your team
- customized to be most relevant to the code that you're building. Next year,
I've got a whole new workshop coming, so keep watching here (and my website:
<a href="http://jeremybytes.com">jeremybytes.com</a>) for future events.
</p>
<p>Happy Coding!</p>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-76070344380205096202023-08-23T08:00:00.002-07:002023-08-23T08:23:10.348-07:00New Video: 'await' Return Types<p>
A new video is available on my YouTube channel: <a href="https://www.youtube.com/watch?v=3kuwLDibFDE">Why do you have to return a Task when you use "await" in a C# method?</a>. The video is a quick walkthrough of code based on an article from earlier
this week (link below).
</p>
<h1 class="style-scope ytd-watch-metadata" style="-webkit-box-orient: vertical; -webkit-line-clamp: 2; background: rgb(255, 255, 255); border: 0px; color: #0f0f0f; display: -webkit-box; font-family: "YouTube Sans", Roboto, sans-serif; font-size: 2rem; line-height: 2.8rem; margin: 0px; max-height: 5.6rem; overflow: hidden; padding: 0px; text-overflow: ellipsis; word-break: break-word;">
<yt-formatted-string class="style-scope ytd-watch-metadata" force-default-style="">Why do you have to return a Task when you use "await" in a C#
method?</yt-formatted-string>
</h1>
<blockquote>
<span style="font-size: large;">Whenever we "await" something in a C# method, the return value is
automatically wrapped in a Task, and the return type for the method must
include the Task as well. This leads to some strange looking code: the code
in the method returns one thing (such as a Person object), but the return
type for the method returns another (a Task of Person). In this video, we
will look at some code to try to understand this a bit better.</span>
</blockquote>
<p>
The code compares using "await" with what happens if we were to use "Task"
manually. The comparison helps my brain process the disconnect between return
types. Hopefully it will help you as well.
</p>
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="BLOG_video_class" height="316" src="https://www.youtube.com/embed/3kuwLDibFDE" width="558" youtube-src-id="3kuwLDibFDE"></iframe>
</div>
<br />
<p>Article:</p>
<p></p>
<ul style="text-align: left;">
<li>
<a href="https://jeremybytes.blogspot.com/2023/08/why-do-you-have-to-return-task-whenever.html">Why Do You Have to Return "Task" Whenever You "await" Something in a
Method in C#?</a>
</li>
</ul>
<p>More videos are on the way.</p>
<p>Happy Coding!</p>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-32985660640319265612023-08-21T08:00:00.027-07:002023-09-07T05:21:59.338-07:00Why Do You Have to Return "Task" Whenever You "await" Something in a Method in C#?There is something that has always bothered me in C#: Whenever you "await"
something in a method, the return value must be wrapped in a Task.
<div><br /></div><div><i><b>Note: If you prefer video to text, take a look here: <a href="https://www.youtube.com/watch?v=3kuwLDibFDE">YouTube: Why do you have to return a Task when you use await in a C# method?</a></b></i></div><div><br /></div>
<h2 style="text-align: left;">The Behavior</h2>
<div>Let's start with an example:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIysOxAwHutmHZKtpdz2zkNvhBts3BpjbBaHirEEDAtrDBjDeinPvtVETn9-iqvxhgewOTKCOiOtagCesXRKiJfljL0CA4UWFgYAfqu2JBvjJK9l_1V0ymVuX7bZUovSez3PIhYbuziD56CjU5-OvFJvp3HKvXCvHY-7KRelSb_S83btEOCCMHCtNxfuE/s982/normal_vs_async.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="453" data-original-width="982" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIysOxAwHutmHZKtpdz2zkNvhBts3BpjbBaHirEEDAtrDBjDeinPvtVETn9-iqvxhgewOTKCOiOtagCesXRKiJfljL0CA4UWFgYAfqu2JBvjJK9l_1V0ymVuX7bZUovSez3PIhYbuziD56CjU5-OvFJvp3HKvXCvHY-7KRelSb_S83btEOCCMHCtNxfuE/w640-h296/normal_vs_async.png" width="640" /></a>
</div>
<br />
<pre><code> public Person GetPerson(int id)
{
List<Person> people = GetPeople();
Person selectedPerson = people.Single(p => p.Id == id);
return selectedPerson;
}
public async Task<Person> GetPersonAsync(int id)
{
List<Person> people = await GetPeopleAsync();
Person selectedPerson = people.Single(p => p.Id == id);
return selectedPerson;
}
</code></pre>
<div><br /></div>
<div>
The first method (GetPerson) does not have any asynchronous code. It calls the
"GetPeople" method that returns a List of Person objects. Then it uses a LINQ
method (Single) to pick out an individual Person. Lastly it returns that person.
As expected, the return type for the "GetPerson" method is "Person".
</div>
<div><br /></div>
<div>
The second method (GetPersonAsync) does have asynchronous code. It calls the
"GetPeopleAsync" method (which returns a Task<List<Person>>) and
then awaits the result. This also gives us a List of Person objects. Then it uses the same LINQ method (Single) to get the
selected Person. Lastly it returns that person.
</div>
<div><br /></div>
<div>
But here's where things are a bit odd: even though our return statement
("return selectedPerson") returns a "Person" type, the method itself
("GetPeopleAsync") returns "Task<Person>".
</div>
<div><br /></div>
<h2 style="text-align: left;">A Leaky Abstraction</h2>
<div>
I love using "await" in my code. It is so much easier than dealing with the
underlying Tasks directly, and it handles the 95% scenario for me (meaning,
95% of the time, it does what I need -- I only need to drop back to using Task
directly when I need more flexibility).
</div>
<div><br /></div>
<div>
I also really like how "await" lets me write asynchronous code very similarly to how I write non-asynchronous code. Exceptions are raised as expected, and I can
use standard try/catch/finally blocks. For the most part, I do not have to
worry about "Task" at all when using "await".
</div>
<div><br /></div>
<div>It is a very good abstraction over Task.</div>
<div><br /></div>
<div>
But it does "leak" in this one spot. A leaky abstraction is one where the
underlying implementation shows through. And the return type of a method that
uses "await" is one spot where the underlying "Task" implementation leaks
through.
</div>
<div><br /></div>
<div>
This isn't necessarily a bad thing. All abstractions leak to a certain extent.
But this particular one has bugged me for quite a while. And it can be difficult
to grasp for developers who may not have worked with Task directly.
</div>
<div><br /></div>
<h2 style="text-align: left;">A Better Understanding</h2>
<div>
Most of the descriptions I've seen have just said "If you use 'await' in your
method, the return type is automatically wrapped in a Task." And there's not
much more in the way of explanation.
</div>
<div><br /></div>
<div>
To get a better understanding of why things work this way, let's get rid of
the abstraction and look at using "Task" directly for this code, building things up one line at a time.</div>
<div><br /></div>
<div>
<i>If you would like more information on how to use Task manually (along with
how to use 'await'), you can take a look at the resources available here:
<a href="http://www.jeremybytes.com/Demos.aspx#TaskAndAwait">I'll Get Back to You: Task, Await, and Asynchronous Methods in C#</a>.</i>
</div>
<div><br /></div>
<h3 style="text-align: left;">Step 1: The Method Signature</h3>
<div>Let's start with the signature that we would like to have:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS33nys2_ZFHhdiAM_ptGWlzEm0GJzC7eWg_TmlxBRL2fTXN-p6ODhpXfwoiBSiKa2pJQ9ljb9zRBZvryS6495UqYPVn2pDx0sS-O9yhr8pUOBWeFAU6My4lxlVVRTrq9XvResqQXUcuYGtUZ1xx8YZm-e0flrJR8imUH1UeTkYja0Aem6f8bWMq79yDc/s726/step1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="132" data-original-width="726" height="116" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS33nys2_ZFHhdiAM_ptGWlzEm0GJzC7eWg_TmlxBRL2fTXN-p6ODhpXfwoiBSiKa2pJQ9ljb9zRBZvryS6495UqYPVn2pDx0sS-O9yhr8pUOBWeFAU6My4lxlVVRTrq9XvResqQXUcuYGtUZ1xx8YZm-e0flrJR8imUH1UeTkYja0Aem6f8bWMq79yDc/w640-h116/step1.png" width="640" /></a>
</div>
<br />
<pre><code> public Person GetPersonAsyncWithTask(int id)
{
}
</code></pre>
<div><br /></div>
<div>
The idea is that I can pass the "id" of a person to this method and get a
Person instance back. So this would be my ideal signature.
</div>
<div><br /></div>
<h3 style="text-align: left;">Step 2: Call the Async Method</h3>
<div>
Next, we'll call the "GetPersonAsync" method, but instead of awaiting it,
we'll get the actual Task back. When I don't know the specific type that comes
back from a method, I'll use "var result" to capture the value and then let
Visual Studio help me. Here's that code:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIaD14ldsLoEOaBCd32enby7nMTLc7dkrYajGcZIeunuPevprjCeje6HeJb5s53VDxlTaRCIATjPthzum6hLh5tCjggISrX4bFBo6p340J7OE5AYfpYxyOr_16Sawt02qs_U7_qiIMZiTdoXx-KvVtdEMkCZHhcKipl2vtx-vCx9pz6VEiYZQP97Wwv8Q/s732/step2a.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="143" data-original-width="732" height="126" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIaD14ldsLoEOaBCd32enby7nMTLc7dkrYajGcZIeunuPevprjCeje6HeJb5s53VDxlTaRCIATjPthzum6hLh5tCjggISrX4bFBo6p340J7OE5AYfpYxyOr_16Sawt02qs_U7_qiIMZiTdoXx-KvVtdEMkCZHhcKipl2vtx-vCx9pz6VEiYZQP97Wwv8Q/w640-h126/step2a.png" width="640" /></a>
</div>
<br />
<pre><code> public Person GetPersonAsyncWithTask(int id)
{
var result = GetPeopleAsync();
}
</code></pre>
<div><br /></div>
<div>
If we hover the cursor over "var", Visual Studio tells us the type we can
expect:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO9J_eoNbVeFGiuyzjiIAJS8O1r9SKBryFq9WLXAu9cCPt04RKKTa1Y_ohgMfPZm9saC-_4DufSA84FAPqjhQS7q8yB7BWU8mvlk_VA7374x86yalILQIec1fF3cK2jMGNMy81vVasKya9W8y3qaGZd7tCKV285035HypaQUwAvAHmNfNBvW2S1EgV3j4/s867/step2b.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="235" data-original-width="867" height="174" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO9J_eoNbVeFGiuyzjiIAJS8O1r9SKBryFq9WLXAu9cCPt04RKKTa1Y_ohgMfPZm9saC-_4DufSA84FAPqjhQS7q8yB7BWU8mvlk_VA7374x86yalILQIec1fF3cK2jMGNMy81vVasKya9W8y3qaGZd7tCKV285035HypaQUwAvAHmNfNBvW2S1EgV3j4/w640-h174/step2b.png" width="640" /></a>
</div>
<br />
<div><br /></div>
<div>
This tells us that "result" is "Task<TResult>" and that "TResult is
List<Person>". This means that "GetPeopleAsync" returns
"Task<List<Person>>". So let's update our code to be more explict:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYPSRwmRx5SxZ0ixkYNnZ8R1N0032UJOlSyg8foQM0Hf9CGDT7lnIevKnU_z_cVm5VDBHEjNUEV8W1ih7M5vj3ryQJv006eG3YVXNTk66A8KZGXXqtUlUT_Qu3vBVuzpPfC9b_aukC9dTsQFVmJYwc5yEjuTgRIR5k-J8TwnQuj-xDZKYngrBZKKMMB00/s870/step2c.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="143" data-original-width="870" height="106" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYPSRwmRx5SxZ0ixkYNnZ8R1N0032UJOlSyg8foQM0Hf9CGDT7lnIevKnU_z_cVm5VDBHEjNUEV8W1ih7M5vj3ryQJv006eG3YVXNTk66A8KZGXXqtUlUT_Qu3vBVuzpPfC9b_aukC9dTsQFVmJYwc5yEjuTgRIR5k-J8TwnQuj-xDZKYngrBZKKMMB00/w640-h106/step2c.png" width="640" /></a>
</div>
<br />
<pre><code> public Person GetPersonAsyncWithTask(int id)
{
Task<List<Person>> peopleTask = GetPeopleAsync();
}
</code></pre>
<div><br /></div>
<div>
Now we have our explicit type, along with a better name for the result:
peopleTask.
</div>
<div><br /></div>
<h3 style="text-align: left;">Step 3: Add a Continuation</h3>
<div>
The next step is to add a continuation to the Task. By adding a continuation,
we are telling our code that after the Task is complete, we would like to
"continue" by doing something else.
</div>
<div><br /></div>
<div>This is done with the "ContinueWith" method on the task:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCPuhY5SSzPHo_JDlP_GFKxorz2IpAZkqkPIK2_SHcvmUP_I8-_9vnFMhU9WClo0hqFh9st-nw2-hDUSxBU4ik7RU4M2SovV3C0ANmOdYY1ZfBGxUu9O5pQtTsjSTSu4kCnkD_4Rz1vJIKAFnMiH5rpPr4uZnthGQlqtjAeOYdHRnh177txqmT_h5qcU0/s876/step3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="257" data-original-width="876" height="188" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCPuhY5SSzPHo_JDlP_GFKxorz2IpAZkqkPIK2_SHcvmUP_I8-_9vnFMhU9WClo0hqFh9st-nw2-hDUSxBU4ik7RU4M2SovV3C0ANmOdYY1ZfBGxUu9O5pQtTsjSTSu4kCnkD_4Rz1vJIKAFnMiH5rpPr4uZnthGQlqtjAeOYdHRnh177txqmT_h5qcU0/w640-h188/step3.png" width="640" /></a>
</div>
<br />
<pre><code> public Person GetPersonAsyncWithTask(int id)
{
Task<List<Person>> peopleTask = GetPeopleAsync();
peopleTask.ContinueWith(task =>
{
});
}
</code></pre>
<div><br /></div>
<div>
The "ContinueWith" method takes a delegate (commonly inlined using a lambda
expression). In this case, the delegate takes the "peopleTask" as a parameter
(which we'll call "task" in the continuation).
</div>
<div><br /></div>
<div>
<i>For more information on delegates and lambda expressions, you can take a
look at the resources available here:
<a href="http://www.jeremybytes.com/Demos.aspx#GF">Get Func-y: Understanding Delegates in C#</a>.</i>
</div>
<div><br /></div>
<h3 style="text-align: left;">Step 4: Fill in the Continuation Code</h3>
<div>
The next step is to fill in the body of our continuation. This is basically
"the rest of the method" that we had in our non-asynchronous version:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigr5k-zdmX6KxK1Zv96t9W72leaFxnV7AIuDitzbOkiWYfETgNP_JH30P84nzdEj9YJxj3Nna0CmRyLkk-lOhGbRvwkfhbVlEvN8GHhhdUDZgtm_2-ShoW_VQFI9zBAFzqbummrx0GQ5kkXyL5F0xzjHoOMxfI6gApF9zWG-f5uuY9-uZUgJ_Z3mVH72o/s1027/step4.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="346" data-original-width="1027" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigr5k-zdmX6KxK1Zv96t9W72leaFxnV7AIuDitzbOkiWYfETgNP_JH30P84nzdEj9YJxj3Nna0CmRyLkk-lOhGbRvwkfhbVlEvN8GHhhdUDZgtm_2-ShoW_VQFI9zBAFzqbummrx0GQ5kkXyL5F0xzjHoOMxfI6gApF9zWG-f5uuY9-uZUgJ_Z3mVH72o/w640-h216/step4.png" width="640" /></a>
</div>
<br />
<pre><code> public Person GetPersonAsyncWithTask(int id)
{
Task<List<Person>> peopleTask = GetPeopleAsync();
peopleTask.ContinueWith(task =>
{
List<Person> people = task.Result;
Person selectedPerson = people.Single(p => p.Id == id);
return selectedPerson;
});
}
</code></pre>
<div><br /></div>
<div>
The first line in the continuation takes the "Result" property of the task
(which is the List<Person>) and assigns it to a variable with a
friendlier name: "people". Then we use the "Single" method like we did above to get an
individual record from the list. Then we return that selected "Person" object.
</div>
<div><br /></div>
<h3 style="text-align: left;">But Now We Have a Problem</h3>
<div>
But now we have a problem: we want to return the "selectedPerson" from the
"GetPersonAsyncWithTask" method, but it is being returned inside the
continuation of the Task instead.
</div>
<div><br /></div>
<div>How do we get this out?</div>
<div><br /></div>
<div>
It turns out that "ContinueWith" returns a value. Let's use the same technique
we used above to figure out what that is.
</div>
<div><br /></div>
<h3 style="text-align: left;">Step 5: Getting a Return from ContinueWith</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrQo2fkPVPKDn3bZmc67R-fXyX-_wEj1lV-ohVuPnps_BW-qnlcb635mHy5Jq817Osz-0ZEly0WUmXriH5EgRnT43B-DorkkT2RI6WXmF9bWSHd72PUHcEvxihW9I83FyIDg9Q66yMkH9W01i12iQpZI6REZAhUJjHRSe93jVOa6W7ZGHMKDKB757o2EM/s1030/step5.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="345" data-original-width="1030" height="214" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrQo2fkPVPKDn3bZmc67R-fXyX-_wEj1lV-ohVuPnps_BW-qnlcb635mHy5Jq817Osz-0ZEly0WUmXriH5EgRnT43B-DorkkT2RI6WXmF9bWSHd72PUHcEvxihW9I83FyIDg9Q66yMkH9W01i12iQpZI6REZAhUJjHRSe93jVOa6W7ZGHMKDKB757o2EM/w640-h214/step5.png" width="640" /></a>
</div>
<br />
<pre><code> public Person GetPersonAsyncWithTask(int id)
{
Task<List<Person>> peopleTask = GetPeopleAsync();
var result = peopleTask.ContinueWith(task =>
{
List<Person> people = task.Result;
Person selectedPerson = people.Single(p => p.Id == id);
return selectedPerson;
});
}
</code></pre>
<div><br /></div>
<div>
Here we have added "var result = " in front of of the call to
"peopleTask.ContinueWith". Then if we hover the mouse over "var", we see that
this is "Task<TResult>" and "TResult is Person". So this tells us that
"ContinueWith" returns a "Task<Person>".
</div>
<div><br /></div>
<div>So let's be more explicit with our variable:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQ2pmxpyq3USZw8ItrOUyDag2uRxrkFKwkLC-Dh3Wuc27BA65Jjwl7jKnz0_Y_nI26Noj5Q8VIt3BmWZ0AW2Fsxe9vF2CQnOHc9U1eQV_yAWW1vQa_qM-oECWwTCb3N6ludSCSofzwQxlRcxfQmiXsyfS62sZiacasxmxPgl3jDvQ3oZZa7r7M-zhuTIk/s1041/step5b.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="352" data-original-width="1041" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQ2pmxpyq3USZw8ItrOUyDag2uRxrkFKwkLC-Dh3Wuc27BA65Jjwl7jKnz0_Y_nI26Noj5Q8VIt3BmWZ0AW2Fsxe9vF2CQnOHc9U1eQV_yAWW1vQa_qM-oECWwTCb3N6ludSCSofzwQxlRcxfQmiXsyfS62sZiacasxmxPgl3jDvQ3oZZa7r7M-zhuTIk/w640-h216/step5b.png" width="640" /></a>
</div>
<br />
<pre><code> public Person GetPersonAsyncWithTask(int id)
{
Task<List<Person>> peopleTask = GetPeopleAsync();
Task<Person> result = peopleTask.ContinueWith(task =>
{
List<Person> people = task.Result;
Person selectedPerson = people.Single(p => p.Id == id);
return selectedPerson;
});
}
</code></pre>
<div><br /></div>
<div>
Now our "result" variable is specifically typed as "Task<Person>".
</div>
<div><br /></div>
<h3 style="text-align: left;">Step 6: Return the Result</h3>
<div>The last step is to return the result:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLZkMAFhunks3dXpeF5VyPo_pBy_4VCC8gtDNGLoZbNBQ2pc6u9fx7Z7OaL9gDgZqC3wiGHyjIK1NliMh98nS0-5BDx7fJhFFycbMN7d1DYTDeeu0vGQsUKNaYve8rdu3el-PqteNeZNN7KupJxGZGoLskaG9aj2F3kDYw3KsXrXJgqrO3WmJYQu38zXE/s1036/step6a.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="382" data-original-width="1036" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLZkMAFhunks3dXpeF5VyPo_pBy_4VCC8gtDNGLoZbNBQ2pc6u9fx7Z7OaL9gDgZqC3wiGHyjIK1NliMh98nS0-5BDx7fJhFFycbMN7d1DYTDeeu0vGQsUKNaYve8rdu3el-PqteNeZNN7KupJxGZGoLskaG9aj2F3kDYw3KsXrXJgqrO3WmJYQu38zXE/w640-h236/step6a.png" width="640" /></a>
</div>
<br />
<pre><code> public Person GetPersonAsyncWithTask(int id)
{
Task<List<Person>> peopleTask = GetPeopleAsync();
Task<Person> result = peopleTask.ContinueWith(task =>
{
List<Person> people = task.Result;
Person selectedPerson = people.Single(p => p.Id == id);
return selectedPerson;
});
return result;
}
</code></pre>
<div><br /></div>
<div>
But we can't stop here. Our return types do not match. The method
(GetPersonAsyncWithTask) returns a "Person", and the actual type we return is
"Task<Person>".
</div>
<div><br /></div>
<div>So we need to update our method signature:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrlVkS3tEt_2HbuGxsZ4d0QjjgnjSDiTfKd0hyrqwOQ6909IsqFqeIO3CDpfBIqgKmKZAljm0E6D0891xP3jqVHpD9c0hi4-zHa9OJGdkEQDqZxH_yERH525IrDGdKYxi7PXPmo9KviQ9jDBzDuWMCAF-w0j9bKn-BdUbCH9oP7UhCrg2YTXcabgSjtpE/s1032/step6b.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="380" data-original-width="1032" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrlVkS3tEt_2HbuGxsZ4d0QjjgnjSDiTfKd0hyrqwOQ6909IsqFqeIO3CDpfBIqgKmKZAljm0E6D0891xP3jqVHpD9c0hi4-zHa9OJGdkEQDqZxH_yERH525IrDGdKYxi7PXPmo9KviQ9jDBzDuWMCAF-w0j9bKn-BdUbCH9oP7UhCrg2YTXcabgSjtpE/w640-h236/step6b.png" width="640" /></a>
</div>
<br />
<pre><code> public Task<Person> GetPersonAsyncWithTask(int id)
{
Task<List<Person>> peopleTask = GetPeopleAsync();
Task<Person> result = peopleTask.ContinueWith(task =>
{
List<Person> people = task.Result;
Person selectedPerson = people.Single(p => p.Id == id);
return selectedPerson;
});
return result;
}
</code></pre>
<div><br /></div>
<div>
Now the method returns a "Task<Person>". <b>And this is what we want</b>. If
someone awaits what comes back from this method, they will get a "Person"
object back. In addition, someone could take this task and set up their own
continuation. This is just the nature of Task and how we set up asynchronous
code using Task.
</div>
<div><br /></div>
<h2 style="text-align: left;">Back to the Comparison</h2>
<div>
So let's go back to our method comparison. But instead of comparing a method
with "await" to a non-asynchronous method, let's compare a method with "await"
to a method that handles the asynchronous Task manually.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTcixR973DVK5zGxqeteoryjxnI7aZ_ItbBtrA8jZlQ517YiEV5cYLFyUyqKwqpnQv8YbnJ0H-UILX2qKItw2-1NKlWXZggEwz5ynVX9pZYwMIgy4GYiMIaj1Q7y3zZCi7wz_HY-u75utArro7RMFZ4T-TgIMUPFqjzonhhYu9FOZGYAad7NpcUNgL8MQ/s1033/await_vs_task.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="620" data-original-width="1033" height="384" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTcixR973DVK5zGxqeteoryjxnI7aZ_ItbBtrA8jZlQ517YiEV5cYLFyUyqKwqpnQv8YbnJ0H-UILX2qKItw2-1NKlWXZggEwz5ynVX9pZYwMIgy4GYiMIaj1Q7y3zZCi7wz_HY-u75utArro7RMFZ4T-TgIMUPFqjzonhhYu9FOZGYAad7NpcUNgL8MQ/w640-h384/await_vs_task.png" width="640" /></a>
</div>
<br />
<pre><code> public async Task<Person> GetPersonAsync(int id)
{
List<Person> people = await GetPeopleAsync();
Person selectedPerson = people.Single(p => p.Id == id);
return selectedPerson;
}
public Task<Person> GetPersonAsyncWithTask(int id)
{
Task<List<Person>> peopleTask = GetPeopleAsync();
Task<Person> result = peopleTask.ContinueWith(task =>
{
List<Person> people = task.Result;
Person selectedPerson = people.Single(p => p.Id == id);
return selectedPerson;
});
return result;
}
</code></pre>
<div><br /></div>
<div>Here's how we can think of this code now (and the compiler magic that happens behind it): anything after the "await" is automatically put into a continuation. What the compiler does is much more complicated, but this is how we can think of it from the programmer's perspective.</div><div><br /></div><div>If we think about it this way, it's easier to see why the method returns "Task<Person>" instead of just "Person".</div><div><br /></div><h2 style="text-align: left;">"await" is Pretty Magical</h2><div>When we use "await" in our code, magical things happen behind the scenes. (And I'm saying that as a good thing.) The compiler gets to figure out all of the details of waiting for a Task to complete and what to do next. When I have this type of scenario (the 95% scenario), then it is pretty amazing. It saves me a lot of hassle and let's me work with code that looks more "normal".</div><div><br /></div><div>The abstraction that "await" gives us is not perfect, though. It does "leak" a little bit. Whenever we "await" something in a method, the return value gets automatically wrapped in a Task. This means that we do need to change the return type of the method to Task. But it is a small price to pay for the ease of using "await".</div><div><br /></div><h2 style="text-align: left;">Wrap Up</h2><div>I've always had a bit of trouble with having to return a Task from any method that "awaits" something. I mean, I knew that I had to do it, but there was always a bit of cognitive dissonance with saying "the code in this method returns 'Person', but the method itself returns 'Task<Person>'."</div><div><br /></div><div>By working through this manually, my brain is a little more comfortable. I have a better understanding of what is happening behind the "await", and that if we were to do everything manually, we would end up returning a Task. So it makes sense that after we add the magic of "await", the method will still need to return a Task.</div><div><br /></div><div>I hope that this has helped you understand things a bit as well. If you have any questions or other ways of looking at this, feel free to leave a comment.</div><div><br /></div><div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com3tag:blogger.com,1999:blog-5359546512544809971.post-6191061536017996742023-08-18T08:47:00.001-07:002023-08-23T08:38:29.556-07:00New Video: Nullable Reference Types and Null Operators in C#<p>
A new video is available on my YouTube channel: <a
href="https://youtu.be/xRxcfSyMD6Y"
>Nullable Reference Types and Null Operators in C#</a
>. The video is a quick walkthrough of code based on a series of articles I
did last year (links below).
</p>
<h2 style="text-align: left;">
Nullable Reference Types and Null Operators in C#
</h2>
<blockquote>
<span style="font-size: large;"
>New projects in C# have nullable reference types enabled by default. This
helps make the intent of our code more clear, and we can catch potential
null references before they happen. But things can get confusing,
particularly when migrating existing projects. In this video, we will look
at the safeguards that nullability provides as well as the problems we still
need to watch out for ourselves. In addition, we will learn about the
various null operators in C# (including null conditional, null coalescing,
and null forgiving operators). These can make our code more expressive and
safe.</span
>
</blockquote>
<p>
The code shows an existing application that can get a null reference exception
at runtime. Then we enable nullable reference types to see what that feature
does and does not do for us. Then we walk through the potential null warnings
and use the various null operators to take care of them.
</p>
<p>All in under 13 minutes!</p>
<div class="separator" style="clear: both; text-align: center;">
<iframe
allowfullscreen=""
class="BLOG_video_class"
height="316"
src="https://www.youtube.com/embed/xRxcfSyMD6Y"
width="558"
youtube-src-id="xRxcfSyMD6Y"
></iframe>
</div>
<br />
<p>
The video is based on a series of articles. If you prefer text (or just want
more detail), you can take a look at these:
</p>
<p></p>
<ul style="text-align: left;">
<li>
<a
href="https://jeremybytes.blogspot.com/2022/07/nullability-in-c-what-it-is-and-what-it.html"
>Nullability in C# - What It Is and What It Is Not</a
>
</li>
<li>
<a
href="https://jeremybytes.blogspot.com/2022/07/null-conditional-operators-in-c-and.html"
>Null Conditional Operators in C# - ?. and ?[]</a
>
</li>
<li>
<a
href="https://jeremybytes.blogspot.com/2022/07/null-forgiving-operator-in-c.html"
>Null Forgiving Operator in C# - !</a
>
</li>
<li>
<a
href="https://jeremybytes.blogspot.com/2022/07/null-coalescing-operators-in-c-and.html"
>Null Coalescing Operators in C# - ?? and ??=</a
>
</li>
<li>
<a
href="https://jeremybytes.blogspot.com/2023/02/c-var-with-reference-types-is-always.html"
>
C# "var" with a Reference Type is Always Nullable
</a>
</li>
</ul>
<p>More videos are on the way.</p>
<p>Happy Coding!</p>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-83148695194112963592023-07-06T12:37:00.002-07:002023-08-23T08:38:46.876-07:00New Video: Fixing Integer Overflow in C# with "checked"<p>
My YouTube channel has been neglected for a while, and I'd like to start
fixing that. So here's a new video:
<a href="https://youtu.be/9mBZvDPyE0U"
>Fixing Integer Overflow in C# with "checked"</a
>.
</p>
<h2 style="text-align: left;">Fixing Integer Overflow in C# with "checked"</h2>
<p></p>
<blockquote>
<span style="font-size: large;">
C# integers overflow by default, meaning if you go past the biggest value of
the integer it will wrap to the smallest value. In this video, we will look
at how we can turn this behavior into an error by using a "checked"
statement, a project-level setting, or a "checked" expression.
</span>
</blockquote>
<p></p>
<p>
This is a fairly quick topic -- just 4 minutes for the basics and a few more
minutes of bonus content.
</p>
<p>
If you prefer reading, you can check out the blog article here:
<a
href="https://jeremybytes.blogspot.com/2023/01/checking-for-overflow-in-c.html"
>Checking for Overflow in C#</a
>.
</p>
<p>
Let me know if you like the format (or not) by dropping a note in the
comments. I am planning on refreshing some of the older topics as well as
covering a few new ones.
</p>
<p>If you find this video helpful, be sure to pass it along.</p>
<div class="separator" style="clear: both; text-align: center;">
<iframe
allowfullscreen=""
class="BLOG_video_class"
height="316"
src="https://www.youtube.com/embed/9mBZvDPyE0U"
width="558"
youtube-src-id="9mBZvDPyE0U"
></iframe>
</div>
<br />
<p>Happy Coding!</p>
<p><br /></p>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-76285802639076681802023-02-22T11:54:00.004-08:002023-02-23T09:06:40.229-08:00C# "var" with a Reference Type is Always Nullable<div>
As an addition to the series on nullability in C#, we should look at the "var"
keyword. This is because "var" behaves a little differently than it did before
nullable reference types. Specifically, when nullability is enabled, using
"var' with a reference type always results in a nullable reference type.
</div>
<div><br /></div>
<h4 style="text-align: left;">Articles</h4>
<ul style="text-align: left;">
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/nullability-in-c-what-it-is-and-what-it.html">Nullability in C# - What It Is and What It Is Not</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-conditional-operators-in-c-and.html">Null Conditional Operators in C# - ?. and ?[]</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-forgiving-operator-in-c.html">Null Forgiving Operator in C# - !</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-coalescing-operators-in-c-and.html">Null Coalescing Operators in C# - ?? and ??=</a>
</li>
<li>
C# "var" with a Reference Type is Always Nullable (<b>this article</b>)
</li>
</ul>
<div>
<i>The source code for this article series can be found on GitHub:
<a href="https://github.com/jeremybytes/nullability-in-csharp">https://github.com/jeremybytes/nullability-in-csharp</a>.</i>
</div>
<div><br /></div>
<h2 style="text-align: left;">The Short Version</h2>
<div>
Using "var" with a reference type always results in a nullable reference type.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgj6lpm-LtwWRLWhAMs2rNRVv5hvyTbR98qhWceiHEvttTYRFvFXHTBJC5Q4Vi9ZiJQKsmOHafyaflvD0_IZhA4FKo6gC7wkcYpDFvheJoJzT2uxkSptFq7dFlgpHU-ntKWmgimd87BoOpuoh0Z4yu-9ZzUIoXV7RU3W79_wB1Kv7mK__5TMoJcO3DH/s877/nullable_list.jpg.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="262" data-original-width="877" height="120" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgj6lpm-LtwWRLWhAMs2rNRVv5hvyTbR98qhWceiHEvttTYRFvFXHTBJC5Q4Vi9ZiJQKsmOHafyaflvD0_IZhA4FKo6gC7wkcYpDFvheJoJzT2uxkSptFq7dFlgpHU-ntKWmgimd87BoOpuoh0Z4yu-9ZzUIoXV7RU3W79_wB1Kv7mK__5TMoJcO3DH/w400-h120/nullable_list.jpg.png" width="400" /></a>
</div>
<code><pre> var people = new List<Person> {...}</pre></code>
<div>
When we hover over the "people" variable name, the pop-up shows us the type is
nullable:
</div>
<code><pre> (local variable) List<Person>? people</pre></code>
<div>
So even though the "people" variable is immediately assigned to using a constructor, the compiler
still marks this as nullable.</div>
<div><br /></div>
<div>
Let's take a look at how this is a little different than it was before.
</div>
<div><br /></div>
<h2 style="text-align: left;">Before Nullable Reference Types</h2>
<div>
Before nullable reference types, we could expect the type of "var" to be the
same as the type of whatever is assigned to the variable.
</div>
<div><br /></div>
<div>Consider the following code:</div>
<code>
<pre> Person person1 = GetPersonById(1);
var person2 = GetPersonById(2);</pre>
</code>
<div>
In this code, both "person1" and "person2" have the same type: "Person"
(assuming that this is the type that comes back from "GetPersonById").
</div>
<div><br /></div>
<div>
We can verify this by hovering over the variable in Visual Studio. First
"person1":
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKVKBTzLyyH_3l0P-pkcqW_lDGP12Qp6hm9Svr5NEB7w64iocNtkqHDmqHSNYW2WhwVMF24q_fzM-CIf90zVqcHexz1M-SYkggvbs0LBReMa1uYDic11x-kxOBmWh-Y4Q-ILZHnixi8L2w1156MTOFMi1yLDUev7-62SXVX6sA9AeSqApaWgFx1Q7j/s712/person1_before.jpg.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="139" data-original-width="712" height="78" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKVKBTzLyyH_3l0P-pkcqW_lDGP12Qp6hm9Svr5NEB7w64iocNtkqHDmqHSNYW2WhwVMF24q_fzM-CIf90zVqcHexz1M-SYkggvbs0LBReMa1uYDic11x-kxOBmWh-Y4Q-ILZHnixi8L2w1156MTOFMi1yLDUev7-62SXVX6sA9AeSqApaWgFx1Q7j/w400-h78/person1_before.jpg.png" width="400" /></a>
</div>
<code><pre> (local variable) Person person1</pre></code>
<div>This shows us that the type of "person1" is "Person".</div>
<div><br /></div>
<div>Now "person2":</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaRHXqhFiJWD11abY2Vlnn3YjUfQnYARo9TfwQQtt7b39QqyrSPgeYDKV59iuXVniRHbr1vRuBJkcjPuKWe97th9ZmODYC7g86ZvaxCaTXopY6nBLRdbWcvhrZPdcMjou8bmRGDVBFublfHnT6xjK4eikciFoBxrZWmLtQo2Z42EIfB87_k-UmqSXo/s697/person2_before.jpg.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="181" data-original-width="697" height="104" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaRHXqhFiJWD11abY2Vlnn3YjUfQnYARo9TfwQQtt7b39QqyrSPgeYDKV59iuXVniRHbr1vRuBJkcjPuKWe97th9ZmODYC7g86ZvaxCaTXopY6nBLRdbWcvhrZPdcMjou8bmRGDVBFublfHnT6xjK4eikciFoBxrZWmLtQo2Z42EIfB87_k-UmqSXo/w400-h104/person2_before.jpg.png" width="400" /></a>
</div>
<code><pre> (local variable) Person person2</pre></code>
<div>This shows us that the type of "person2" is also "Person".</div>
<div><br /></div>
<div>
The lines of code are equivalent. "var" just acts as a shortcut here. And for
those of us who have been coding with C# for a while, this is what we were
used to.
</div>
<div><br /></div>
<div>
<i>If you would like a closer look at "var", you can take a look at "<a href="https://jeremybytes.blogspot.com/2014/02/demystifying-var-keyword-in-c.html">Demystifying the 'var' Keyword in C#</a>".</i>
</div>
<div><br /></div>
<h2 style="text-align: left;">After Nullable Reference Types</h2>
<div>
But things are a bit different when nullable reference types are enabled.
</div>
<div><br /></div>
<div>Here is the same code:</div>
<code>
<pre> Person person1 = GetPersonById(1);
var person2 = GetPersonById(2);</pre>
</code>
<div>
When we hover over the "person1" variable, Visual Studio tells us that this is
a "Person" type (what we expected.
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidXkKDGGHeRANDsKUl_Zlmjh3uq4wv1Y16XXud_jIHFZZDTUhSuguXgFkH1hds7-bW85anwMey1VjRXXphzshXdEKrv1ftybQjvQutw1u2GJ6HYXzZKsA-gEfQswXOpPgNszNuXBPM1l4xwijOWFB5hWv1Da2huP7M2OEaIUigohhS52EKp-3KRk2r/s706/person1_after.jpg.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="142" data-original-width="706" height="80" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEidXkKDGGHeRANDsKUl_Zlmjh3uq4wv1Y16XXud_jIHFZZDTUhSuguXgFkH1hds7-bW85anwMey1VjRXXphzshXdEKrv1ftybQjvQutw1u2GJ6HYXzZKsA-gEfQswXOpPgNszNuXBPM1l4xwijOWFB5hWv1Da2huP7M2OEaIUigohhS52EKp-3KRk2r/w400-h80/person1_after.jpg.png" width="400" /></a>
</div>
<code><pre> (local variable) Person person1</pre></code>
<div>
When we hover over the "person2" variable, Visual Studio tells us that this is
a "Person?" type -- meaning it is a nullable Person.
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcmiFMwTtyeGVjVP-FLWtXMSScmRNo7WjCBzHGalwDuzAFdVG9FACxomE-LWpPfAD_YohyHyMsfeLIGBH29imY93_zQo0S5FaKz71bQKlGtL4PbKKQ5xp8_5gS2zKpR74MAsKIilfonAPo7knBxpiZ3sgnSO0gs4vRbcbzFiwtHn-Tjdi-xhKbvCPN/s694/person2_after.jpg.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="171" data-original-width="694" height="99" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcmiFMwTtyeGVjVP-FLWtXMSScmRNo7WjCBzHGalwDuzAFdVG9FACxomE-LWpPfAD_YohyHyMsfeLIGBH29imY93_zQo0S5FaKz71bQKlGtL4PbKKQ5xp8_5gS2zKpR74MAsKIilfonAPo7knBxpiZ3sgnSO0gs4vRbcbzFiwtHn-Tjdi-xhKbvCPN/w400-h99/person2_after.jpg.png" width="400" /></a>
</div>
<code><pre> (local variable) Person? person2</pre></code>
<div>
So this shows us that the type is different when we use "var". In the code
above, "GetPersonById" returns a non-nullable Person. But as we saw in the
<a href="https://jeremybytes.blogspot.com/2022/07/nullability-in-c-what-it-is-and-what-it.html">first article in the series</a>, that is not something that we can rely on at runtime.
</div>
<div><br /></div>
<h2 style="text-align: left;">The Same with Constructors</h2>
<div>
You might think that this behavior only applies when we assign a variable
based on a method or function call, but the behavior is the same when we use a
constructor during assignment.
</div>
<div><br /></div>
<div>
In the following code, we create a variable called "people" and initialize it
using the constructor for "List<Person>":
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgj6lpm-LtwWRLWhAMs2rNRVv5hvyTbR98qhWceiHEvttTYRFvFXHTBJC5Q4Vi9ZiJQKsmOHafyaflvD0_IZhA4FKo6gC7wkcYpDFvheJoJzT2uxkSptFq7dFlgpHU-ntKWmgimd87BoOpuoh0Z4yu-9ZzUIoXV7RU3W79_wB1Kv7mK__5TMoJcO3DH/s877/nullable_list.jpg.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="262" data-original-width="877" height="120" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgj6lpm-LtwWRLWhAMs2rNRVv5hvyTbR98qhWceiHEvttTYRFvFXHTBJC5Q4Vi9ZiJQKsmOHafyaflvD0_IZhA4FKo6gC7wkcYpDFvheJoJzT2uxkSptFq7dFlgpHU-ntKWmgimd87BoOpuoh0Z4yu-9ZzUIoXV7RU3W79_wB1Kv7mK__5TMoJcO3DH/w400-h120/nullable_list.jpg.png" width="400" /></a>
</div>
<code><pre> var people = new List<Person> {...}</pre></code>
<div>
When we hover over the "people" variable name, the pop-up shows us the type is
nullable:
</div>
<code><pre> (local variable) List<Person>? people</pre></code>
<div>
So even though the "people" variable is assigned based on a constructor call,
the compiler still marks this as nullable.
</div>
<div><br /></div>
<h2 style="text-align: left;">Confusing Tooling</h2>
<div>
One reason why this was surprising to me is that I generally use the Visual
Studio tooling a bit differently then I have used it here. And the pop-ups
show different things depending on what we are looking at.
</div>
<div><br /></div>
<div><b>Hover over "var"</b></div>
<div>
Normally when I want to look at a type of a "var" variable, I hover over the
"var" keyword. Here is the result:
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKcA3EcKJ1m6BUK9kelweGh3UxuSCQvwnJfxunfUtSSFcXUwBiZ3NCzhMICen5wm1NZOsMVRf8hykCc47QiZflrFRkI0MGaWo3s8RvESlLR1o0xKWqt68XwFJ2Z31oenj0PTLSTMI5ZNeAoV_BYefMnpvuuzRAXs3ewZ7nu_wHrFNpFL-JB3AXSpsj/s1510/inspecting_var.jpg.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="271" data-original-width="1510" height="114" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKcA3EcKJ1m6BUK9kelweGh3UxuSCQvwnJfxunfUtSSFcXUwBiZ3NCzhMICen5wm1NZOsMVRf8hykCc47QiZflrFRkI0MGaWo3s8RvESlLR1o0xKWqt68XwFJ2Z31oenj0PTLSTMI5ZNeAoV_BYefMnpvuuzRAXs3ewZ7nu_wHrFNpFL-JB3AXSpsj/w640-h114/inspecting_var.jpg.png" width="640" /></a>
</div>
<code>
<pre>class System.Collections.Generic.List<T>
...
T is Person</pre>
</code>
<div><br /></div>
<div>
This tells us that the type of "var" is "List<T>" where "T is Person".
This means "List<Person>". Notice that there is no mention of
nullability here.
</div>
<div><br /></div>
<div><b>Hover over the variable name</b></div>
<div>
However, if we hover over the name of the variable itself, we get the actual
type:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFr5hGw-R0KGuketXbcr-TOpd0DUt3I66-rjvtwQB6ikB1b3rF75TwKlJ_Up1I-JY7PA0q0wlMOhvopRqn6GWM24CuDd2AZJ7ysKyT-6DM7fVSDVFd1Ys3xmNzTGvjQxrDN0H3SXEpKLgMR2ssl-6HJqZ_tvRFhgg3qq18g5TIjA3bSJB0mDjVizxt/s879/inspecting_variable.jpg.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="265" data-original-width="879" height="120" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFr5hGw-R0KGuketXbcr-TOpd0DUt3I66-rjvtwQB6ikB1b3rF75TwKlJ_Up1I-JY7PA0q0wlMOhvopRqn6GWM24CuDd2AZJ7ysKyT-6DM7fVSDVFd1Ys3xmNzTGvjQxrDN0H3SXEpKLgMR2ssl-6HJqZ_tvRFhgg3qq18g5TIjA3bSJB0mDjVizxt/w400-h120/inspecting_variable.jpg.png" width="400" /></a>
</div>
<code><pre> (local variable) List<Person>? people</pre></code>
<div>
As we've seen above, this shows that our variable is, in fact, nullable.
</div>
<div><br /></div><h2 style="text-align: left;">The Documentation</h2><div>There's documentation on this behavior on the Microsoft Learn Site: <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/declarations#implicitly-typed-local-variables">Declaration Types: Implicitly Typed Variables</a>. This gives some insight into the behavior:</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUZFSW7cCVSRrnfBT1GvdEEF-tSCxGUvu1MdjghY4U5umaPIqepvthKfGIw9clqfya9nTbWVPYxcuJ-jNeP08g1M4x4cct8Nl_JB0_REO5qn0RK-0PXlvPPqF_b6XDYd73_tyGFw92kq16w5r9Ro0AL1wDfvRUeUsczHbMQ5vyCqwVUeFTM07TaZ_o/s907/doc_note.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="403" data-original-width="907" height="285" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUZFSW7cCVSRrnfBT1GvdEEF-tSCxGUvu1MdjghY4U5umaPIqepvthKfGIw9clqfya9nTbWVPYxcuJ-jNeP08g1M4x4cct8Nl_JB0_REO5qn0RK-0PXlvPPqF_b6XDYd73_tyGFw92kq16w5r9Ro0AL1wDfvRUeUsczHbMQ5vyCqwVUeFTM07TaZ_o/w640-h285/doc_note.png" width="640" /></a></div><div><br /></div><div></div><blockquote><div><span style="font-size: medium;">"When var is used with nullable reference types enabled, it always implies a nullable reference type even if the expression type isn't nullable. The compiler's null state analysis protects against dereferencing a potential null value. If the variable is never assigned to an expression that maybe null, the compiler won't emit any warnings. If you assign the variable to an expression that might be null, you must test that it isn't null before dereferencing it to avoid any warnings."</span></div><div></div></blockquote><div>This tells us that the behavior supports the compiler messages about potential null values. Because there is so much existing code that uses var, there was a lot of potential to overwhelm devs with "potential null" messages when nullability is enabled. To alleviate that, "var" was made nullable.</div><div><br /></div>
<h2 style="text-align: left;">Should I Worry?</h2>
<div>
The next question is whether we need to worry about this behavior. The answer
is: <b>probably not</b>. In most of our code, we will probably not notice the
difference. Even though the "var" types are technically nullable, they will
most likely not be null (since the variables get an initial assignment).
</div>
<div><br /></div>
<div>
But it may be something to keep in the back of your mind when working with
"var" and nullable reference types. If you are used to using "var" in a lot of
different situations, you just need to be aware that those variables are now
nullable. I know of at least 1 developer who did run into an issue with this, but I have not heard about widespread concerns.</div>
<div><br /></div>
<h2 style="text-align: left;">Wrap Up</h2>
<div>
It's always "fun" when behavior of code changes from what we are used to. I
did not know that this behavior existed for a long time -- not until after I
saw some high-profile folks talking about it online about 2 months ago. I finally put this article together because I am working on a presentation about nullable reference types that I'll be giving in a couple months.</div>
<div><br /></div>
<div>
My use of "var" is not changing due to this. Historically, if the type was
clear based on the assignment (such as a constructor), then I would tend to
use "var". If the type was not clear based on the assignment (such as coming
from a not-too-well-named method call), then I tend to be more explicit it my
types.
</div>
<div><br /></div>
<div>
Of course, a lot of this changed with <a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/target-typed-new">"target-typed new" expressions</a> (from C# 9). But I won't go into my thoughts on that right now.
</div>
<div><br /></div>
<div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com1tag:blogger.com,1999:blog-5359546512544809971.post-4497696530856821252023-01-17T15:43:00.000-08:002023-01-17T15:43:20.303-08:00Checking for Overflow in C#<p>
By default, integral values (such as int, uint, and long) do not throw an
exception when they overflow. Instead, they "wrap" -- and this is probably not
a value that we want. But we can change this behavior so that an exception is
thrown instead.
</p>
<p>Short version:</p>
<blockquote>
<p style="text-align: left;">
<span style="font-size: large;"><b>Use a "checked" statement to throw an overflow exception when an
integral overflow (or underflow) occurs.</b></span>
</p>
</blockquote>
<p>
As an alternate, there is also a project property that we can
set: <b>CheckForOverflowUnderflow</b>.
</p>
<p>
<i>Note: This behavior applies to "enum" and "char" which are also integral
types in C#.</i>
</p>
<p>Let's take a closer look at this.</p>
<h2 style="text-align: left;">Default Behavior</h2>
<p>
Let's start by looking at the default overflow behavior for integer (Int32) --
this applies to other integral types as well.
</p>
<code>
<pre> int number = int.MaxValue;
Console.WriteLine($"number = {number}");
number++;
Console.WriteLine($"number = {number}");</pre>
</code>
<p>
This creates a new integer variable ("number") and sets its value to the
maximum value that a 32-bit integer can hold.<br />
</p>
<p>
When we increment that value (using the ++ operator), it goes past that
maximum value. But instead of throwing an error, the value "wraps" to the
lowest integer value.
</p>
<p>Here is the output of the above code:</p>
<code>
<pre> number = 2147483647
number = -2147483648</pre>
</code>
<p>This is probably not the behavior that we want.</p>
<h2 style="text-align: left;">Using a "checked" Statement</h2>
<p>
One way we can fix this is to use a "checked" statement. This consists of the
"checked" operator and a code block.
</p>
<code>
<pre> int number = int.MaxValue;
Console.WriteLine($"number = {number}");
checked
{
number++;
}
Console.WriteLine($"number = {number}");
</pre>
</code>
<p>
The result of this is that the increment operation is checked to see if it
will overflow. If it does, it throws an OverflowException.
</p>
<p>Here's the exception if we run this code from Visual Studio:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi86zO0raKEA6PybLWrF7sp1jIyfeVRB_-QU6ghqljnaIoht7taxltpZXwYa-BV1lnudJQYhRC2bwZolmxUABLBo-zrb17vE46RIHpWogB-Imh7CuugMy6qq3sTyY86J87vX9PxHFV4ERg_cdANMkRceo0c9u-0sMnMiM9dWYIp4tP1ufRbaJJ8w11Z/s782/OverflowException.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="367" data-original-width="782" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi86zO0raKEA6PybLWrF7sp1jIyfeVRB_-QU6ghqljnaIoht7taxltpZXwYa-BV1lnudJQYhRC2bwZolmxUABLBo-zrb17vE46RIHpWogB-Imh7CuugMy6qq3sTyY86J87vX9PxHFV4ERg_cdANMkRceo0c9u-0sMnMiM9dWYIp4tP1ufRbaJJ8w11Z/w640-h300/OverflowException.png" width="640" /></a>
</div>
<code>
<pre> System.OverflowException: 'Arithmetic operation resulted in an overflow.'</pre>
</code>
<p>Let's handle the exception:</p>
<code>
<pre> try
{
int number = int.MaxValue;
Console.WriteLine($"number = {number}");
checked
{
number++;
}
Console.WriteLine($"number = {number}");
}
catch (OverflowException ex)
{
Console.WriteLine($"OVERFLOW: {ex.Message}");
}</pre>
</code>
<p>Now our output looks like this:</p>
<code>
<pre> number = 2147483647
OVERFLOW: Arithmetic operation resulted in an overflow.</pre>
</code>
<p>
If we overflow an integer value, it probably means that we need to make some
changes to the code (such as using a larger type). But the good news is that
an overflow exception makes sure that we do not unintentionally use an
invalid value.
</p>
<h2 style="text-align: left;">Using "CheckForOverflowUnderflow"</h2>
<p>
We may want to apply overflow checks through our entire project. Instead of
using individual "checked" statements, we can also set a property on the
project: "CheckForOverflowUnderflow".
</p>
<p>
To see this in action, we will removed the "checked" statement from our code:
</p>
<code>
<pre> try
{
int number = int.MaxValue;
Console.WriteLine($"number = {number}");
number++;
Console.WriteLine($"number = {number}");
}
catch (OverflowException ex)
{
Console.WriteLine($"OVERFLOW: {ex.Message}");
}</pre>
</code>
<p>
The code does not throw an exception, and we are back to the wrapped value:
</p>
<code>
<pre> number = 2147483647
number = -2147483648</pre>
</code>
<p>
In the project, we can add the "CheckForOverflowUnderflow" property. Here is
the project file for our basic console application:
</p>
<code>
<pre> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace>check_overflow</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<span style="background-color: #fff2cc;"><CheckForOverflowUnderflow>true</CheckForOverflowUnderflow></span>
</PropertyGroup>
</Project></pre>
</code>
<p>
We've set the "CheckForOverflowUnderflow" property to "true". Now when the
application runs, the exception is thrown.
</p>
<code>
<pre> number = 2147483647
OVERFLOW: Arithmetic operation resulted in an overflow.</pre>
</code>
<h2 style="text-align: left;">"unchecked"</h2>
<p>
In addition to "checked", there is also an "unchecked" operator. As you might
imagine, this does not check for overflow.
</p>
<p>
So with our project set to "CheckForOverflowUnderflow", we can add an
"unchecked" block that will ignore the project setting.
</p>
<code>
<pre> try
{
int number = int.MaxValue;
Console.WriteLine($"number = {number}");
unchecked
{
number++;
}
Console.WriteLine($"number = {number}");
}
catch (OverflowException ex)
{
Console.WriteLine($"OVERFLOW: {ex.Message}");
}</pre>
</code>
<p>
The code does not throw an exception, and we are back to the wrapped value:
</p>
<code>
<pre> number = 2147483647
number = -2147483648</pre>
</code>
<h2 style="text-align: left;">Wrap Up</h2>
<p>
Normally, I do not need to worry about overflow or underflow in my code; it's
not something that comes up in my applications very often.
</p>
<p>
One exception is with Fibonacci sequences. I've written a few articles
involving Fibonacci sequences (including "<a href="https://jeremybytes.blogspot.com/2017/04/implementing-fibonacci-sequence-with.html">Implementing a Fibonacci Sequence with Value Tuples in C# 7</a>" and "<a href="https://jeremybytes.blogspot.com/2021/09/coding-practice-learning-rust-with.html">Coding Practice: Learning Rust with Fibonacci Numbers</a>"). Since the sequence more or less doubles on each item, it overflows a
32-bit integer very quickly (around the 46th item in the sequence). This is
one place where I generally use a larger type (like a long) and also a
"checked" statement to make sure I do not end up using invalid values in my
code somewhere.
</p>
<p>
"checked" statements do come with a cost. There is a little overhead that is
added in the arithmetic operations. Because of this, I generally leave
projects with the default setting ("unchecked" for the project), and then use
targeted "checked" statements where I need them.
</p>
<p>
It's always interesting to find things like this in the C# language. I don't
often need it, but it's really good to have it when I do.
</p>
<p>Happy Coding!</p>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-50509428591294536122023-01-16T15:52:00.005-08:002023-02-21T11:12:57.779-08:00Full-Day Workshop - Asynchronous & Parallel Programming in C# (March 2023)<p>
I'll be giving a full day workshop (with hands-on labs) at Visual Studio LIVE!, Las Vegas on March 19th, 2023. Now's your chance to spend a full day with me
and also learn tons of useful tech during the rest of the week.
</p>
<h3 style="text-align: left;">Event Info:</h3>
<blockquote style="border: none; margin: 0px 0px 0px 40px; padding: 0px; text-align: left;">
Visual Studio LIVE! March 19-24, 2023<br />
Planet Hollywood Resort & Casino<br />
Las Vegas NV<br />
Event Link: <a href="https://vslive.com/events/las-vegas-2023/home.aspx">https://vslive.com/events/las-vegas-2023/home.aspx</a>
<br />
</blockquote>
<p></p><blockquote><span style="font-size: large;"><b>Use the promo code "Clark" to save $500 off the regular price for 4-,
5-, or 6-day packages (Note: you'll need the 6-day package to join me for the
full day workshop on Sunday, March 19). Here's a direct link to registration
that includes the promo code: <a href="http://bit.ly/3HSDHJn">http://bit.ly/3HSDHJn</a></b></span></blockquote><a href="http://bit.ly/3HSDHJn"></a>
<p></p>
<p>Read on to see what we'll learn in the workshop.</p>
<h2 style="text-align: left;">
Hands-on Lab: Asynchronous and Parallel Programming in C#
</h2>
<p>3/19/2023 9:00 a.m. - 6:00 p.m.<br />Level: <i>Intermediate</i></p>
<p>
Asynchronous programming is a critical skill to take full advantage of today's
multi-core systems. But async programming brings its own set of issues. In
this workshop, we'll work through some of those issues and get comfortable
using parts of the .NET Task Parallel Library (TPL).
</p>
<p>
We'll start by calling asynchronous methods using the Task Asynchronous
Pattern (TAP), including how to handle exceptions and cancellation. With this
in hand, we'll look at creating our own asynchronous methods and methods that
use asynchronous libraries. Along the way, we'll see how to avoid deadlocks,
how to isolate our code for easier async, and why it's important to stay away
from "asyc void".
</p>
<p>
In addition, we'll look at some patterns for running code in parallel,
including using Parallel.ForEachAsync, channels, and other techniques. We'll
see pros and cons so that we can use the right pattern for a particular
problem.
</p>
<p>
Throughout the day, we'll go hands-on with lab exercises to put these skills
into practice.
</p>
<h3 style="text-align: left;">Objectives:</h3>
<p></p>
<ul style="text-align: left;">
<li>Use asynchronous methods with Task and await </li>
<li>Create asynchronous methods and libraries </li>
<li>How to avoid deadlocks and other pitfalls </li>
<li>Understand different parallel programming techniques</li>
</ul>
<h3 style="text-align: left;">Topics:</h3>
<p>Here's a list of some of the topics that we'll cover:</p>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYFhxysxxZT0LKutpLGsHlSCA3Q8SjhtleNT-ly8PZSYOQQV3uuuTsPhq85UjA5JAMdauoVD8zCZVQxsoEMWtYhpfd3Dg2avc3z-z8jTsrMuIgOJKgPID0CTlc6bIwkjn-pErliPIzIDLCsxv90zR3ox6k3xCLmIDmnXaiQjukaZ6i3c-Xoqug7IMy/s1518/async-topics.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="765" data-original-width="1518" height="322" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjYFhxysxxZT0LKutpLGsHlSCA3Q8SjhtleNT-ly8PZSYOQQV3uuuTsPhq85UjA5JAMdauoVD8zCZVQxsoEMWtYhpfd3Dg2avc3z-z8jTsrMuIgOJKgPID0CTlc6bIwkjn-pErliPIzIDLCsxv90zR3ox6k3xCLmIDmnXaiQjukaZ6i3c-Xoqug7IMy/w640-h322/async-topics.png" width="640" /></a>
</div>
<p></p>
<h3 style="text-align: left;">Pre-Requisites:</h3>
<p>
Basic understanding of C# and object-oriented programming (classes,
inheritance, methods, and properties). No prior experience with asynchronous
programming is necessary; we'll take care of that as we go.
</p>
<h3 style="text-align: left;">Attendee Requirements:</h3>
<ul style="text-align: left;">
<li>
You must provide your own laptop computer (Windows, Mac, or Linux) for this
hands-on lab.
</li>
<li>
All other laptop requirements will be provided to attendees 2 weeks prior to
the conference
</li>
</ul>
<h3 style="text-align: left;">Hope to See You There!</h3><p>If you can't make it to this one, keep watching here (and my website: <a href="http://jeremybytes.com">jeremybytes.com</a>) for future public workshops. I'm also available for private workshops for your team - customized to be most relevant to the code that you're building.</p>
<p>Happy Coding!</p>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-46087636569723004462022-07-20T10:00:00.422-07:002023-02-22T12:02:26.212-08:00Null Coalescing Operators in C# - ?? and ??=<div>
Over the last few articles, we have looked at nullability in C#. When we have
nullability enabled, we need to respond to warnings that come up in our code.
Sometimes when we come across a null value, we would like to replace it with a
default non-null value. This is something that the null coalescing operator (2
question marks) can help us with.
</div>
<div><br /></div>
<h4 style="text-align: left;">Articles</h4>
<ul style="text-align: left;">
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/nullability-in-c-what-it-is-and-what-it.html">Nullability in C# - What It Is and What It Is Not</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-conditional-operators-in-c-and.html">Null Conditional Operators in C# - ?. and ?[]</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-forgiving-operator-in-c.html">Null Forgiving Operator in C# - !</a>
</li>
<li>
Null Coalescing Operators in C# - ?? and ??= (<b>this article</b>)
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2023/02/c-var-with-reference-types-is-always.html">
C# "var" with a Reference Type is Always Nullable
</a>
</li>
</ul>
<div>
<i>The source code for this article series can be found on GitHub:
<a href="https://github.com/jeremybytes/nullability-in-csharp">https://github.com/jeremybytes/nullability-in-csharp</a>.</i>
</div>
<div><br /></div>
<h2 style="text-align: left;">The Short Version</h2>
<div>
When we have a null value, we often want to replace it with a non-null default
value. The null coalescing operator can help us with this.
</div>
<div><br /></div>
<div>Here's an example of a traditional way to write this code:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4E2oC6MSr6jzIoTx0cwMVqojMGFsiSaHCKN5vEHPGwiCStGTJNDEeZZV8GepfRZu9hXUOIzqdC8V-8GJvjZhKA6x2jNwJyGT6gMFvtozLXHpfO4v-_l80ww_Kcq8lIRRu9rKOopDb412XwM9pqqzL_4nnoz135fy_KtoWhVnmwKzpXunJbYS0Dw2-/s457/Snippet_with_if.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="116" data-original-width="457" height="101" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4E2oC6MSr6jzIoTx0cwMVqojMGFsiSaHCKN5vEHPGwiCStGTJNDEeZZV8GepfRZu9hXUOIzqdC8V-8GJvjZhKA6x2jNwJyGT6gMFvtozLXHpfO4v-_l80ww_Kcq8lIRRu9rKOopDb412XwM9pqqzL_4nnoz135fy_KtoWhVnmwKzpXunJbYS0Dw2-/w400-h101/Snippet_with_if.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> if (result is null)
return new List<Person>();
return result;</code></pre>
<div><br /></div>
<div>
With the null coalescing operator, we can reduce this to a single line that
include the null check and the non-null default value:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLnjvHZ9TubNVePo1HRhFlyGgqyyt-yB5pEAu7Mh97b6rog194Fzuufp9YBSE7bciUy84yk8SG9fucRTC5ViAHw1Y5RuY6MxC4LQtymIJeyCQ-IzTqycTPk46nsl-fdFgJVM1tn6ic3Jb83sGyc7MecDtUab6tVqCOP2druLsYWpcOKDrYYP5CWFEY/s546/snippet.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="56" data-original-width="546" height="41" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLnjvHZ9TubNVePo1HRhFlyGgqyyt-yB5pEAu7Mh97b6rog194Fzuufp9YBSE7bciUy84yk8SG9fucRTC5ViAHw1Y5RuY6MxC4LQtymIJeyCQ-IzTqycTPk46nsl-fdFgJVM1tn6ic3Jb83sGyc7MecDtUab6tVqCOP2druLsYWpcOKDrYYP5CWFEY/w400-h41/snippet.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> return result ?? new List<Person>();</code></pre>
<div><br /></div>
<div>
If "result" <b>is not null</b>, "result" is returned. If "result"
<b>is null</b>, a new empty list is returned.
</div>
<div><br /></div>
<div>Let's look at this in more detail.</div>
<div><br /></div>
<h2 style="text-align: left;">Possible Null Return</h2>
<div>
In the last article (about the null forgiving operator), we looked at a
possible null return value for a method. We will start with that same method.
The code is available in the GitHub repository for this article
series: <a href="https://github.com/jeremybytes/nullability-in-csharp">https://github.com/jeremybytes/nullability-in-csharp</a>.
</div>
<div><br /></div>
<div>
<i>The "StartingCode" folder has the starting state of the code at the
beginning of the first article in the series. The "FinishedCode" has the
completed code. The links in this article will point to the "FinishedCode"
folder unless otherwise noted.</i>
</div>
<div><br /></div>
<div>
As a reminder, the "GetAsync" method needs to return a non-null value, but in
the "StartingCode", we get a possible null warning. (This is in the
<a href="https://github.com/jeremybytes/nullability-in-csharp/blob/main/StartingCode/UsingTask.Library/PersonReader.cs">"PersonReader.cs" file</a>
of the "UsingTask.UI" project.)
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOPg33KHMFmnfzkPkX5tJl5LhlTfmja69yK-8xjMnpesze_sfapfjkmDbyDaYmvT8hRzVGkOmdv4EPD8cRl3twEX3n7UPLQEEbF8WVM3TjbAvuSOwphYD3frIvs4qF0tecQwIS0hIV_rkyqDEcbcXkky1FMhabsxmMIZVoqmAepWrHPDqzFFqb16pi/s1152/null_return_warning.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="372" data-original-width="1152" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOPg33KHMFmnfzkPkX5tJl5LhlTfmja69yK-8xjMnpesze_sfapfjkmDbyDaYmvT8hRzVGkOmdv4EPD8cRl3twEX3n7UPLQEEbF8WVM3TjbAvuSOwphYD3frIvs4qF0tecQwIS0hIV_rkyqDEcbcXkky1FMhabsxmMIZVoqmAepWrHPDqzFFqb16pi/w640-h206/null_return_warning.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> public async Task<List<Person>> GetAsync(
CancellationToken cancelToken = new CancellationToken())
{
[collapsed code]
var result = JsonSerializer.Deserialize<List<Person>>(stringResult, options);
return result;
}</code></pre>
<pre><code> 'result' may be null here.
CS8603: Possible null reference return.</code></pre>
<div><br /></div>
<div>
The "GetAsync" method returns a "Task<List<Person>>". The
"List<Person>" part is non-nullable.
</div>
<div><br /></div>
<div>
But because of the way the JsonSerializer works, we may end up with a "null"
in the "result" variable. Because of this, we get a warning that we have a
"possible null reference return".
</div>
<div><br /></div>
<div><b>Using a Guard Clause</b></div>
<div>
As we saw in earlier articles, the traditional way of dealing with possible
nulls is to set up a guard clause that lets us handle things appropriately. We
can fix this code with an "if" statement:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4E2oC6MSr6jzIoTx0cwMVqojMGFsiSaHCKN5vEHPGwiCStGTJNDEeZZV8GepfRZu9hXUOIzqdC8V-8GJvjZhKA6x2jNwJyGT6gMFvtozLXHpfO4v-_l80ww_Kcq8lIRRu9rKOopDb412XwM9pqqzL_4nnoz135fy_KtoWhVnmwKzpXunJbYS0Dw2-/s457/Snippet_with_if.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="116" data-original-width="457" height="101" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4E2oC6MSr6jzIoTx0cwMVqojMGFsiSaHCKN5vEHPGwiCStGTJNDEeZZV8GepfRZu9hXUOIzqdC8V-8GJvjZhKA6x2jNwJyGT6gMFvtozLXHpfO4v-_l80ww_Kcq8lIRRu9rKOopDb412XwM9pqqzL_4nnoz135fy_KtoWhVnmwKzpXunJbYS0Dw2-/w400-h101/Snippet_with_if.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> if (result is null)
return new List<Person>();
return result;</code></pre>
<div><br /></div>
<div>
In this code, we first check to see if "result" is null. If it is, then we
return a new empty list. If "result" is not null, then we return "result"
directly. (Since the first "return" exits out of the method, we do not need an
"else" block. We will only hit the second return if the "if" evaluates to
false.)
</div>
<div><br /></div>
<div>
Just like with the null conditional operator that we saw previously, we can
get rid of the explicit guard clause by using the null coalescing operator.
</div>
<div><br /></div>
<h2 style="text-align: left;">Null Coalescing Operator - ??</h2>
<div>
The null coalescing operator is 2 question marks. Here is the same code block
using this operator:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLnjvHZ9TubNVePo1HRhFlyGgqyyt-yB5pEAu7Mh97b6rog194Fzuufp9YBSE7bciUy84yk8SG9fucRTC5ViAHw1Y5RuY6MxC4LQtymIJeyCQ-IzTqycTPk46nsl-fdFgJVM1tn6ic3Jb83sGyc7MecDtUab6tVqCOP2druLsYWpcOKDrYYP5CWFEY/s546/snippet.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="56" data-original-width="546" height="41" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLnjvHZ9TubNVePo1HRhFlyGgqyyt-yB5pEAu7Mh97b6rog194Fzuufp9YBSE7bciUy84yk8SG9fucRTC5ViAHw1Y5RuY6MxC4LQtymIJeyCQ-IzTqycTPk46nsl-fdFgJVM1tn6ic3Jb83sGyc7MecDtUab6tVqCOP2druLsYWpcOKDrYYP5CWFEY/w400-h41/snippet.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> return result ?? new List<Person>();</code></pre>
<div><br /></div>
<div>
Here is how this operator works. Whatever is on the left side of the "??"
operator (in this case the "result" variable) is checked for a "null". If the
value
<b>is not null</b>, then that value is returned. If the value <b>is null</b>,
then whatever is on the right side of the "??" operator (in this case "new
List<Person>()") is returned.
</div>
<div><br /></div>
<div>
This works when we want to assign a value to a variable or field as well as
when we want to return a value. Let's look at another example that assigns the
result to a field.
</div>
<div><br /></div>
<h2 style="text-align: left;">Property with a Default Value</h2>
<div>
For this example, we will go back to the "MainWindow.xaml.cs" file in the
"UsingTask.UI" project.
</div>
<div><br /></div>
<div>
Here is the code that we will start with (this block is specifically from
<a href="https://github.com/jeremybytes/nullability-in-csharp/blob/main/StartingCode/UsingTask.UI/MainWindow.xaml.cs">the file in the "StartingCode" folder</a>):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi03KlTfFXcPu5ZdW9rbLAoi79iSlan1VS3qnHP0P2u6v62gwaFHAWJf5OzjgxLWtwXQ0HFtyNlurI-Zi1jPv9kDQ73Oapl-huli20KESY_pY5zoCnl6p9reqB5LWjDZOghbnk54TVxe1XrnLyMTSNfg-R_GSedvAUsemH66oiV2l5ZVdEsQ8BzFwZW/s498/Property01.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="407" data-original-width="498" height="328" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi03KlTfFXcPu5ZdW9rbLAoi79iSlan1VS3qnHP0P2u6v62gwaFHAWJf5OzjgxLWtwXQ0HFtyNlurI-Zi1jPv9kDQ73Oapl-huli20KESY_pY5zoCnl6p9reqB5LWjDZOghbnk54TVxe1XrnLyMTSNfg-R_GSedvAUsemH66oiV2l5ZVdEsQ8BzFwZW/w400-h328/Property01.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> public MainWindow()
{
InitializeComponent();
reader = new PersonReader();
}
private PersonReader? reader;
public PersonReader Reader
{
get => reader;
set => reader = value;
}</code></pre>
<div><br /></div>
<div>
This code has a non-null "Reader" property that uses a nullable "reader"
field. (We'll go into the reasoning behind this in just a bit.) The
constructor initializes the "reader" field to a non-null value.
</div>
<div><br /></div>
<div>
Even though the "reader" property is not null when the class is created, we
still get a warning in the "Reader" getter because the backing field is
nullable (and it could be set to null elsewhere in the code).
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNVzBpB418vyQruDeSfjgvgx8yMkR2pqBYRisE-EYgWhUz41FIxaUS7ilU8u-3yVFvVkAsWECHWpzds0xk9UtuZCUnP5CQOljvnwcRyDD8FsHyo93XA7sfPAokMFpoUi1VZrLE0_51Bos4xicJEafJpDK4ZCGxRH7dkajolsxcce6aisMq9HdHDjWq/s677/Property_warning.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="290" data-original-width="677" height="171" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNVzBpB418vyQruDeSfjgvgx8yMkR2pqBYRisE-EYgWhUz41FIxaUS7ilU8u-3yVFvVkAsWECHWpzds0xk9UtuZCUnP5CQOljvnwcRyDD8FsHyo93XA7sfPAokMFpoUi1VZrLE0_51Bos4xicJEafJpDK4ZCGxRH7dkajolsxcce6aisMq9HdHDjWq/w400-h171/Property_warning.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> 'reader' may be null here.
CS8603: Possible null reference return.</code></pre>
<h2 style="text-align: left;">Moving the Property Initialization</h2>
<div>
As a first step, let's move the field initialization out of the constructor
and put it into the property getter.
</div>
<div><br /></div>
<div>
For this, we will use a traditional guard clause (from the
<a href="https://github.com/jeremybytes/nullability-in-csharp/blob/main/FinishedCode/UsingTask.UI/MainWindow.xaml.cs">"MainWindow.xaml.cs" file</a>
of the "UsingTask.UI" project in the "FinishedCode" folder).
</div>
<div><br /></div>
<div>
<i>We will walk through several steps to get to a final state. This will help
us better understand the code that we end up with. If you look at the file
in the "FinishedCode" folder, you will find each of the steps in comment
blocks.</i>
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilj-C2rV4v22juCos45StZ6g8IAfBspIDxgr7vEadmVd0DfbXSt2UmMuNQlfoShMQSqa9a2oh_TUEuZ6LtREA9OUpm66EeWy1eqtqP8jE6R2uP7firHvoR32pNyNJlFqkxJ0pLKmg133sI6vpisjTGvAs7G4cbMKoZhl71TeVTgag44QLuvZOV_ODr/s568/Property02.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="556" data-original-width="568" height="391" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEilj-C2rV4v22juCos45StZ6g8IAfBspIDxgr7vEadmVd0DfbXSt2UmMuNQlfoShMQSqa9a2oh_TUEuZ6LtREA9OUpm66EeWy1eqtqP8jE6R2uP7firHvoR32pNyNJlFqkxJ0pLKmg133sI6vpisjTGvAs7G4cbMKoZhl71TeVTgag44QLuvZOV_ODr/w400-h391/Property02.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> public MainWindow()
{
InitializeComponent();
}
private PersonReader? reader;
public PersonReader Reader
{
get
{
if (reader is null)
reader = new PersonReader();
return reader;
}
set => reader = value;
}</code></pre>
<div><br /></div>
<div>
The getter for the property first checks the backing field to see if it is
null. If it is null, then it assigns a value to it and then returns it. This
ensures that the "Reader" property will never return a null.
</div>
<div><br /></div>
<div><b>Why Would We Do This?</b></div>
<div>
You're probably wondering why we would change the code this way. The
difference is pretty subtle. With either of the blocks of code, the "reader"
field gets initialized. The difference is <b>when</b> the field is
initialized.
</div>
<div><br /></div>
<div>
In the first example, the field is initialized in the constructor. So it is
set when the class is instantiated.
</div>
<div><br /></div>
<div>
In the second example, the field is initialized the first time the "Reader"
property is accessed. This means that it may never get set (if we never use
the "Reader" property).
</div>
<div><br /></div>
<div>
One place I have used this pattern is with property injection. Property
injection is a good way to have a default value for a dependency that you can
still swap out for unit testing. You can read more about a specific scenario
where I've used this pattern here:
<a href="https://jeremybytes.blogspot.com/2015/06/property-injection-simple-vs-safe.html">Property Injection: Simple vs. Safe</a>. (Hint: It involves serial ports that I do not want to initialize unless I
actually use them.)
</div>
<div><br /></div>
<div>
So assuming this is a pattern that we want to use, let's move on to
integrating the null coalescing operator.
</div>
<div><br /></div>
<h2 style="text-align: left;">Adding the Null Coalescing Operator</h2>
<div>
Just like with our earlier example, we can remove the guard clause by using
the null coalescing operator:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9hP55hcbVWIlUV-stZO0WbCw9KSZnhLgCTamGxzniy-QnYxc8sbbATRaPVdcNU-BhogbWOIN9emDqYJQnsjJJIE_Okjr5-uyFkryM0om-G_RKfdqfhV8BHeI1g5KxvfNaaOvj3HLvOzRh713p7pBdxHezKeJcBEZV7dUfFWtFt5ZnfTCeVttOltHd/s675/Property03.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="346" data-original-width="675" height="205" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9hP55hcbVWIlUV-stZO0WbCw9KSZnhLgCTamGxzniy-QnYxc8sbbATRaPVdcNU-BhogbWOIN9emDqYJQnsjJJIE_Okjr5-uyFkryM0om-G_RKfdqfhV8BHeI1g5KxvfNaaOvj3HLvOzRh713p7pBdxHezKeJcBEZV7dUfFWtFt5ZnfTCeVttOltHd/w400-h205/Property03.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> private PersonReader? reader;
public PersonReader Reader
{
get
{
reader = reader ?? new PersonReader();
return reader;
}
set => reader = value;
}</code></pre>
<div><br /></div>
<div>
This code uses the "??" operator to check whether the "reader" field is null.
If it <b>is not null</b>, then "reader" gets assigned to itself (so no
change). If it <b>is null</b>, then a "new PersonReader()" is assigned to the
"reader" field.
</div>
<div><br /></div>
<div>
Either way, the "reader" that is returned in the property getter will not be
null.
</div>
<div><br /></div>
<h2 style="text-align: left;">Combining Operators - ??=</h2>
<div>
The next step is to combine operators. The title and introduction make it look
like "??" and "??=" are 2 separate operators. But technically they are not.
Let's take a step back to look at something more familiar.
</div>
<div><br /></div>
<div>To increment a value, we can use the following code:</div>
<pre><code> int a = 3;
a = a + 2;</code></pre>
<div>
This uses the "+" operator to add 2 to "a" and then assign the result back to
"a". The effect is that the "a" variable is increased by 2.
</div>
<div><br /></div>
<div>
But we can combine the "+" operator with the "=" operator to create the
following:
</div>
<pre><code> int a = 3;
a += 2;</code></pre>
<div>This has the same effect: the "a" variable is increased by 2.</div>
<div><br /></div>
<div>
We can do the same thing with the "??" operator. We can combine the null
coalescing operator (??) with the assignment operator (=) to get ??=.
</div>
<div><br /></div>
<div>Here's what that looks like in code:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL4wOo5JVzXDyAvWPVnpe4pbEzBLZBBF0Du86BwGOzlSNF8-LCknCMeUjGct9H7awZ8Mo_S10Vf96BEp9hNGF2IETNcHIy1oHLVTq91yg4wT8-Gt47KmnWDUHxJ0x2O_Uo3dy8F1FohEArlsmJqqKhZU4dcT_-qMxMUPUTfqoI2FPj84oaOXSmEMU2/s572/Property04.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="338" data-original-width="572" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgL4wOo5JVzXDyAvWPVnpe4pbEzBLZBBF0Du86BwGOzlSNF8-LCknCMeUjGct9H7awZ8Mo_S10Vf96BEp9hNGF2IETNcHIy1oHLVTq91yg4wT8-Gt47KmnWDUHxJ0x2O_Uo3dy8F1FohEArlsmJqqKhZU4dcT_-qMxMUPUTfqoI2FPj84oaOXSmEMU2/w400-h236/Property04.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> private PersonReader? reader;
public PersonReader Reader
{
get
{
reader ??= new PersonReader();
return reader;
}
set => reader = value;
}</code></pre>
<div><br /></div>
<div>
This will do the null check on the "reader" field. If it <b>is not null</b>,
then the value of "reader" is unchanged. But if "reader" <b>is null</b>, then
a new PersonReader is assigned to it.
</div>
<div><br /></div>
<div>
So just like with a +=, we can use ??= to combine the null coalescing operator
with the assignment operator.
</div>
<div><br /></div>
<h2 style="text-align: left;">Making Things Shorter</h2>
<div>
We can further condense the code by combining the assignment of the "reader"
field with the return of that field. This is where the code may get a bit
confusing (which is why we're going in small steps):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbuCpBbVBjT4n1I4mOWHuplYM9180ThI3Gp-FjA10QKFVdV1QPnHLme7nanO1tZ23hPKVtaQXkcdt8N8lzen4A85jBYhEz4rcPGqyqucF3UkpSKjtgJpa-11TGiWXpCyKNSHxjKbxVxEAFm4_Dkx7YFLvfnQS7GlO5zV0Fi5qs1DXmWKpAF4T8eOsM/s717/Property05.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="210" data-original-width="717" height="118" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbuCpBbVBjT4n1I4mOWHuplYM9180ThI3Gp-FjA10QKFVdV1QPnHLme7nanO1tZ23hPKVtaQXkcdt8N8lzen4A85jBYhEz4rcPGqyqucF3UkpSKjtgJpa-11TGiWXpCyKNSHxjKbxVxEAFm4_Dkx7YFLvfnQS7GlO5zV0Fi5qs1DXmWKpAF4T8eOsM/w400-h118/Property05.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> private PersonReader? reader;
public PersonReader Reader
{
get { return reader ??= new PersonReader(); }
set => reader = value;
}</code></pre>
<div><br /></div>
<div>
In our earlier example, we used the null coalescing operator to decide to
return "result" (if it was not null) or a new empty list (if it was null).
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLnjvHZ9TubNVePo1HRhFlyGgqyyt-yB5pEAu7Mh97b6rog194Fzuufp9YBSE7bciUy84yk8SG9fucRTC5ViAHw1Y5RuY6MxC4LQtymIJeyCQ-IzTqycTPk46nsl-fdFgJVM1tn6ic3Jb83sGyc7MecDtUab6tVqCOP2druLsYWpcOKDrYYP5CWFEY/s546/snippet.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="56" data-original-width="546" height="41" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLnjvHZ9TubNVePo1HRhFlyGgqyyt-yB5pEAu7Mh97b6rog194Fzuufp9YBSE7bciUy84yk8SG9fucRTC5ViAHw1Y5RuY6MxC4LQtymIJeyCQ-IzTqycTPk46nsl-fdFgJVM1tn6ic3Jb83sGyc7MecDtUab6tVqCOP2druLsYWpcOKDrYYP5CWFEY/w400-h41/snippet.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> return result ?? new List<Person>();</code></pre>
<div><br /></div>
<div>
We could do this with the property getter, but it would not set the "reader"
field. So if the "reader" field was null, it would stay that way.
</div>
<div><br /></div>
<div>
By using the combined operator "??=" the "reader" field is checked for null
<b>and</b> assigned a value if it is null. Then the value of "reader" is
returned.
</div>
<div><br /></div>
<div>It does take a little getting used to.</div>
<div><br /></div>
<pre><code> get { return reader ??= new PersonReader(); }</code></pre>
<div><br /></div>
<div>
We just have to remember that "??=" does both a null check and a possible
assignment.
</div>
<div><br /></div>
<div><b>One Last Step: Expression-Bodied Getter</b></div>
<div>
As long as we are making things short, we can make this a bit smaller by using
an expression-bodied member for the getter.
</div>
<div><br /></div>
<div>Here's what that code looks like:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhExmMpIOb5lpckdnFYEKQJnz7yjm9GhzgV5VosJExSd6fSCb8E6bIj78fCwcZi2L_C9I3Nht901XD8reQ1FwGwekMEYn-crhLM21SwJFSiEEFNMg5PhEO89K7JhTdIWCI5v6niJeXo9Sg7AYb8MWFsXsdgC-yHoM-Ta9J3QGe1nO8SJFgOBa-aWBEd/s601/Property06.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="210" data-original-width="601" height="140" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhExmMpIOb5lpckdnFYEKQJnz7yjm9GhzgV5VosJExSd6fSCb8E6bIj78fCwcZi2L_C9I3Nht901XD8reQ1FwGwekMEYn-crhLM21SwJFSiEEFNMg5PhEO89K7JhTdIWCI5v6niJeXo9Sg7AYb8MWFsXsdgC-yHoM-Ta9J3QGe1nO8SJFgOBa-aWBEd/w400-h140/Property06.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> private PersonReader? reader;
public PersonReader Reader
{
get => reader ??= new PersonReader();
set => reader = value;
}</code></pre>
<div><br /></div>
<div>
Since our getter only has a single expression (meaning one line of code that
returns a value), we can use the "=>" operator and remove the curly braces
and "return" keyword.
</div>
<div><br /></div>
<div>
Expression-bodied members are a bit outside the scope of this article. But
hopefully this syntax will make more sense if you run into it in someone
else's code.
</div>
<div><br /></div>
<h2 style="text-align: left;">Wrap Up</h2>
<div>
The null coalescing operator (??) is very useful in eliminating nulls from our
code. We can check for a null and provide a non-null default value. This way,
we know that the result will not be null.
</div>
<div><br /></div>
<div>
In addition, we can combine the null coalescing operator with the assignment
operator - ??=. This gives us the ability to check a variable or field for
"null" and then assign a valid default to it instead.
</div>
<div><br /></div>
<div>
When we enable nullability in our projects, Visual Studio gives us
compile-time warnings to help us eliminate unintended nulls. We can use the
various null operators (null conditional, null forgiving, and null coalescing)
to address those warnings.
</div>
<div><br /></div>
<div>
The result is that we have code that is less likely to throw null reference
exceptions at runtime. (And that means less debugging!)
</div>
<div><br /></div>
<div>
<i>Be sure to check the GitHub repository for the code samples and links to
all of the articles:
<a href="https://github.com/jeremybytes/nullability-in-csharp">https://github.com/jeremybytes/nullability-in-csharp</a>.</i>
</div>
<div><br /></div>
<div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-39685255259305777902022-07-19T10:00:00.289-07:002023-02-22T12:02:36.894-08:00Null Forgiving Operator in C# - !<div>
Over the last couple articles, we have been looking at nullability in C#. When
we have nullability enabled, we need to respond to warnings that come up in
our code. Sometimes these warnings are incorrect (after all, the analyzers are
not infallible). Fortunately for us, there is a null forgiving operator
(represented by an exclamation point) that we can use to say that we want to
ignore one of these incorrect warnings.
</div>
<div><br /></div>
<h4 style="text-align: left;">Articles</h4>
<ul style="text-align: left;">
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/nullability-in-c-what-it-is-and-what-it.html">Nullability in C# - What It Is and What It Is Not</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-conditional-operators-in-c-and.html">Null Conditional Operators in C# - ?. and ?[]</a>
</li>
<li>Null Forgiving Operator in C# - ! (<b>this article</b>)</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-coalescing-operators-in-c-and.html">Null Coalescing Operators in C# - ?? and ??=</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2023/02/c-var-with-reference-types-is-always.html">
C# "var" with a Reference Type is Always Nullable
</a>
</li>
</ul>
<div>
<i>The source code for this article series can be found on GitHub: <a href="https://github.com/jeremybytes/nullability-in-csharp">https://github.com/jeremybytes/nullability-in-csharp</a>.</i>
</div>
<div><br /></div>
<h2 style="text-align: left;">The Short Version</h2>
<div>
Sometimes the warnings that we get around nullability are incorrect. When we
are confident that an object will not be null, we can use the null forgiving
operator (also called the null suppressing operator) to remove the warning. We
do this by using an exclamation point.
</div>
<div><br /></div>
<div>
In the following example, the analyzer thinks that the "Exception" property on
task may be null. We can tell the analyzer that we know it is not null by
using the null forgiving operator.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1Z87T8Wu_xvGQS35jwItqHp-85IJrE38tgKmlOtbslUKhchMzP7Ry7OtBofXbmOBZYzNMOBv3B_HpXewMgqkfFCzscvlhveOJ7GkszeyeRXhAN87mIrjGNXQzOU53j4jK02pCrAlGHstWWdiKAkzF0fROPYDRxeBVrns04K9-TkJ_wOpftk7BImAM/s597/Snippet_line.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="51" data-original-width="597" height="34" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1Z87T8Wu_xvGQS35jwItqHp-85IJrE38tgKmlOtbslUKhchMzP7Ry7OtBofXbmOBZYzNMOBv3B_HpXewMgqkfFCzscvlhveOJ7GkszeyeRXhAN87mIrjGNXQzOU53j4jK02pCrAlGHstWWdiKAkzF0fROPYDRxeBVrns04K9-TkJ_wOpftk7BImAM/w400-h34/Snippet_line.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> task.Exception!.Flatten().InnerExceptions</code></pre>
<div>
If we add the "!" after the "Exception" property, the warning is supressed.
</div>
<div><br /></div>
<div>
I tend to think of this as <b>the I-know-what-I'm-doing operator</b>. Let's
take a closer look at the code to better understand this.
</div>
<div><br /></div>
<h2 style="text-align: left;">Incorrect Warning</h2>
<div>
For the most part, the warnings that we get when nullability is enabled are
pretty accurate. But sometimes, there is not enough information, and a warning
is incorrect. We'll use the same sample code from the prior articles: <a href="https://github.com/jeremybytes/nullability-in-csharp">https://github.com/jeremybytes/nullability-in-csharp</a>.
</div>
<div><br /></div>
<div>
<i>As a reminder, the "StartingCode" folder has the starting state of the code
at the beginning of the first article in the series. The "FinishedCode" has
the completed code. The links in this article will point to the
"FinishedCode" folder unless otherwise noted.</i>
</div>
<div><br /></div>
<div>
Here's an example from inside the "FetchWithTaskButton_Click" method in the
<a href="https://github.com/jeremybytes/nullability-in-csharp/blob/main/FinishedCode/UsingTask.UI/MainWindow.xaml.cs">"MainWindow.xaml.cs" file</a> of the "UsingTask.UI" project:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEBToWfmgqEplEFRh3b5BOsooa474CJIADV4-8eyRuRd26F4FxmZ2WA1mFaQ-1b-I7L0qdasp7CuTYz955VBcog1Wd_ojcYoz1Q7HWS4gX5sLCUfIDo6rPSwr5KNOc8VDkXoyyBGOCJEdCiM7J3U7VVocKzjQxP95aAz9KbvPWqPacak-Ab5r4jlWo/s935/Warning1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="168" data-original-width="935" height="114" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEBToWfmgqEplEFRh3b5BOsooa474CJIADV4-8eyRuRd26F4FxmZ2WA1mFaQ-1b-I7L0qdasp7CuTYz955VBcog1Wd_ojcYoz1Q7HWS4gX5sLCUfIDo6rPSwr5KNOc8VDkXoyyBGOCJEdCiM7J3U7VVocKzjQxP95aAz9KbvPWqPacak-Ab5r4jlWo/w640-h114/Warning1.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> if (task.IsFaulted)
{
foreach (var ex in task.Exception.Flatten().InnerExceptions)
Log(ex);
}</code></pre>
<div><br /></div>
<div>
The "Exception" property on the "task" variable is nullable. So we get a
warning:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP76J30hVtUqFw3MxloMBCuKgFJPt8kEVgiSj9oYyYcrWan_YL3Vrto8szpJYOP7jh_BviMTOlNij54r5czmctsMY0DbV575UVPSJJTMuslHnweePdYgI8gGZpPLGX30V8Xqf2C7AANXUzoc1sSSqDYf3L1g_uIJubIjk9VA18_UY_94oakK6UjdN8/s1438/Warning2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="420" data-original-width="1438" height="186" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP76J30hVtUqFw3MxloMBCuKgFJPt8kEVgiSj9oYyYcrWan_YL3Vrto8szpJYOP7jh_BviMTOlNij54r5czmctsMY0DbV575UVPSJJTMuslHnweePdYgI8gGZpPLGX30V8Xqf2C7AANXUzoc1sSSqDYf3L1g_uIJubIjk9VA18_UY_94oakK6UjdN8/w640-h186/Warning2.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> 'Exception' may be null here.
CS8602: Dereference of a possibly null reference.</code></pre>
<div><br /></div>
<div>
This is the same warning that we have seen in previous articles. But in this
case, the warning is incorrect.
</div>
<div><br /></div>
<div><b>Task.IsFaulted</b></div>
<div>
The reason this is incorrect is because of the "if" statement that checks the
"IsFaulted" property on "task".
</div>
<div><br /></div>
<div>
When a task is faulted, it means that an exception was thrown during the task
execution. If the "IsFaulted" property is true, then the "Exception" property
will be populated (meaning, it is <b>not</b> null).
</div>
<div><br /></div>
<div>
So even though the "Exception" property is nullable, we know that it will not
be null in this block of code.
</div>
<div><br /></div>
<div>
One way of fixing this is to add a "#pragma" to the code to suppress the
warning. Personally, I really don't like this approach because it clutters up
the code. (If you are curious, you can use "Ctrl+." to have Visual Studio add
the "#pragma" for you.)
</div>
<div><br /></div>
<div>
Fortunately, there is another solution: use the null forgiving operator.
</div>
<div><br /></div>
<h2 style="text-align: left;">Null Forgiving Operator - !</h2>
<div>
With the null conditional operator (from the last article), we add a question
mark after the object that may be null. The null forgiving operator uses an
exclamation point.
</div>
<div><br /></div>
<div>Here is the updated code:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCp94ZlVcMa3Eavu8u1oQDPuRJ7r-5_sw0qU_uceXxc5hcSUsx5f0y-kWearxmB6_yZIadPhuPwhZnthgUCfX6pl1pTSyt0vHa0qxBjnbZWwXaLhIpKbXu-XJA_MgCWs-gaX75x0RHVicqkuPvJAycsoT4Cj6IsS7dN0kDE3luTlOZN769FhfzrER5/s936/Null_forgiving.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="170" data-original-width="936" height="116" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCp94ZlVcMa3Eavu8u1oQDPuRJ7r-5_sw0qU_uceXxc5hcSUsx5f0y-kWearxmB6_yZIadPhuPwhZnthgUCfX6pl1pTSyt0vHa0qxBjnbZWwXaLhIpKbXu-XJA_MgCWs-gaX75x0RHVicqkuPvJAycsoT4Cj6IsS7dN0kDE3luTlOZN769FhfzrER5/w640-h116/Null_forgiving.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> if (task.IsFaulted)
{
foreach (var ex in task.Exception!.Flatten().InnerExceptions)
Log(ex);
}</code></pre>
<div><br /></div>
<div>
With the operator added, the warning is suppressed. This is a way of saying "I
know what I'm doing. This object is not going to be null here".
</div>
<div><br /></div>
<h2 style="text-align: left;">Another Example</h2>
<div>
As another example, let's look at the return value of a method, specifically
the "GetAsync" method in the
<a href="https://github.com/jeremybytes/nullability-in-csharp/blob/main/StartingCode/UsingTask.Library/PersonReader.cs">"PersonReader.cs" file</a>
of the "UsingTask.UI" project. (Note: this snippet is from the "StartingCode"
folder.)
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOPg33KHMFmnfzkPkX5tJl5LhlTfmja69yK-8xjMnpesze_sfapfjkmDbyDaYmvT8hRzVGkOmdv4EPD8cRl3twEX3n7UPLQEEbF8WVM3TjbAvuSOwphYD3frIvs4qF0tecQwIS0hIV_rkyqDEcbcXkky1FMhabsxmMIZVoqmAepWrHPDqzFFqb16pi/s1152/null_return_warning.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="372" data-original-width="1152" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOPg33KHMFmnfzkPkX5tJl5LhlTfmja69yK-8xjMnpesze_sfapfjkmDbyDaYmvT8hRzVGkOmdv4EPD8cRl3twEX3n7UPLQEEbF8WVM3TjbAvuSOwphYD3frIvs4qF0tecQwIS0hIV_rkyqDEcbcXkky1FMhabsxmMIZVoqmAepWrHPDqzFFqb16pi/w640-h206/null_return_warning.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> public async Task<List<Person>> GetAsync(
CancellationToken cancelToken = new CancellationToken())
{
[collapsed code]
var result = JsonSerializer.Deserialize<List<Person>>(stringResult, options);
return result;
}</code></pre>
<pre><code> 'result' may be null here.
CS8603: Possible null reference return.</code></pre>
<div><br /></div>
<div>
The "GetAsync" method returns a "Task<List<Person>>". The
"List<Person>" part is non-nullable.
</div>
<div><br /></div>
<div>
The "Deserialize" method on the "JsonDeserializer" may return null (depending
on the string that is being parsed).
</div>
<div><br /></div>
<div>
So in the code sample, the "result" variable may be null. Because of this, we
get a warning that we have a "possible null reference return".
</div>
<div><br /></div>
<div>
In this case, I'm an not particularly concerned about the "Deserialize" method
returning null. This is a known service (that I control), and the method has
checks in place to make sure the service call is successful.
</div>
<div><br /></div>
<div><b>Ignoring the Warning</b></div>
<div>
We can use the null forgiving operator to ignore the warning by putting the
exclamation mark after the "result".
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz4ie3F2MzAAMa_NbD5LdPqVzUYiKJ5v0HKephxZXWDIq7zEtQhII9YRMQwpsMuZ5PSQaJfXH7io9lie4pZDh9RrS8hm7kdYQilAL9SG58Q-hSv8AXsgI8vBDAtCByeDJ_3KAPz3PgdD8cXxhm5_hLu-cu4pL2Rjzzt8idNft9Kt1-JggSRXRCsVOM/s1166/null_return_forgiven.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="272" data-original-width="1166" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz4ie3F2MzAAMa_NbD5LdPqVzUYiKJ5v0HKephxZXWDIq7zEtQhII9YRMQwpsMuZ5PSQaJfXH7io9lie4pZDh9RrS8hm7kdYQilAL9SG58Q-hSv8AXsgI8vBDAtCByeDJ_3KAPz3PgdD8cXxhm5_hLu-cu4pL2Rjzzt8idNft9Kt1-JggSRXRCsVOM/w640-h150/null_return_forgiven.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> public async Task<List<Person>> GetAsync(
CancellationToken cancelToken = new CancellationToken())
{
[collapsed code]
var result = JsonSerializer.Deserialize<List<Person>>(stringResult, options);
return result!;
}</code></pre>
<div><br /></div>
<div>
This suppresses the null warning. But this may not be the best option for this
code.
</div>
<div><br /></div>
<div><b>Null Coalescing Operator - ??</b></div>
<div>Another option is to use the null coalescing operator:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqeZkEHT6cWdCiwTPFiWdo-ulLrm1CeFbGZwCV1DVlT3BdYbZsSFLmO2EEQnRv9pCmbGDlYyM-HE4Z8s94-5-_D6AM2pXvmzpeWgBQjAnI5-d4qdGX0jiYpLeUVz0fcP5kEz02U07530_iypt-WK4pcS-ID3X4P2Lhju9Ziv5c5Pf-Yj8VaUw-xVn3/s1162/null_coalescing.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="271" data-original-width="1162" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqeZkEHT6cWdCiwTPFiWdo-ulLrm1CeFbGZwCV1DVlT3BdYbZsSFLmO2EEQnRv9pCmbGDlYyM-HE4Z8s94-5-_D6AM2pXvmzpeWgBQjAnI5-d4qdGX0jiYpLeUVz0fcP5kEz02U07530_iypt-WK4pcS-ID3X4P2Lhju9Ziv5c5Pf-Yj8VaUw-xVn3/w640-h150/null_coalescing.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> public async Task<List<Person>> GetAsync(
CancellationToken cancelToken = new CancellationToken())
{
[collapsed code]
var result = JsonSerializer.Deserialize<List<Person>>(stringResult, options);
return result ?? new List<Person>();
}</code></pre>
<div><br /></div>
<div>
Instead of suppressing the warning, the null coalescing operator says "if
'result' is null, then return a new empty list."
</div>
<div><br /></div>
<div>
We will take a look at the null coalescing operator in the next article.
</div>
<div><br /></div>
<h2 style="text-align: left;">Wrap Up</h2>
<div>
The null analyzers are not always accurate. Sometimes we get null warnings on
objects that we know will not be null. In these cases, we can use the null
forgiving operator to suppress the warnings.
</div>
<div><br /></div>
<div>
<div>
Enabling nullability gives us warnings to help us eliminate possible nulls
in our code. And the null operators (including the null forgiving operator)
can help us in dealing with those warnings.
</div>
<div><br /></div>
<div>
In the next article, we will look at the null coalescing operators. These
give us additional tools when dealing with possible nulls in our code. Be
sure to check back details.
</div>
<div><br /></div>
<div>
<b>Next Article:</b> <a href="https://jeremybytes.blogspot.com/2022/07/null-coalescing-operators-in-c-and.html">Null Coalescing Operators in C# - ?? and ??=</a>
</div>
<div><br /></div>
</div>
<div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-73141679304350078092022-07-18T10:00:00.145-07:002023-02-22T12:02:47.696-08:00Null Conditional Operators in C# - ?. and ?[]<div>
In the last article, we took at look at
<a href="https://jeremybytes.blogspot.com/2022/07/nullability-in-c-what-it-is-and-what-it.html">Nullability in C# - What It Is and What It Is Not</a>. When we have nullability enabled, we need to respond to the warnings that
come up in our code. This often means performing null checks or giving the
compiler hints to what our code is doing. Fortunately, we have a number of
null operators in C# to help us with those tasks. To continue with the series
of articles exploring nullability, we will look at the null conditional
operators (represented by question mark dot '?.' and question mark square
brackets '?[]').
</div>
<div><br /></div>
<h4 style="text-align: left;">Articles</h4>
<ul style="text-align: left;">
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/nullability-in-c-what-it-is-and-what-it.html">Nullability in C# - What It Is and What It Is Not</a>
</li>
<li>Null Conditional Operators in C# - ?. and ?[] (<b>this article</b>)</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-forgiving-operator-in-c.html">Null Forgiving Operator in C# - !</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-coalescing-operators-in-c-and.html">Null Coalescing Operators in C# - ?? and ??=</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2023/02/c-var-with-reference-types-is-always.html">
C# "var" with a Reference Type is Always Nullable
</a>
</li>
</ul>
<div>
<i>The source code for this article series can be found on GitHub: <a href="https://github.com/jeremybytes/nullability-in-csharp">https://github.com/jeremybytes/nullability-in-csharp</a>.</i>
</div>
<div><br /></div>
<h2 style="text-align: left;">The Short Version</h2>
<div>
The null conditional operators give us a shortened syntax for checking for a
null object before calling a method, reading a property, indexing into an
object, or accessing another member on a nullable object.
</div>
<div><br /></div>
<div>
<i>Note: We will focus on the ?. operator for the samples since this is the
more common of the 2 operators.</i>
</div>
<div><br /></div>
<div>Let's look at 2 blocks of code that are almost equivalent.</div>
<div><br /></div>
<div>
This first version checks to make sure that the "tokenSource" field is not
null before calling the "Cancel" method. If the field is null, then "Cancel"
is not called:
</div>
<div>
<div><br /></div>
<div></div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ0B20sCrKMTmwuARVygheABRqaIZBqRr4tJdYM58nhRjpnVQ3TPDmYn4verCiKxuCCdUWwlDBvQ870EK4ASxHnipgWe6vy32FOxG4ExVPGSo-kXIDJDrB57UXNlqj2zmMz9s_IerDtAlmth1p2lbXfDdSMmcYZD2_pRiJNjXUK6EHbtsSFSbABRS6/s933/Cancel_button_if.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="233" data-original-width="933" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ0B20sCrKMTmwuARVygheABRqaIZBqRr4tJdYM58nhRjpnVQ3TPDmYn4verCiKxuCCdUWwlDBvQ870EK4ASxHnipgWe6vy32FOxG4ExVPGSo-kXIDJDrB57UXNlqj2zmMz9s_IerDtAlmth1p2lbXfDdSMmcYZD2_pRiJNjXUK6EHbtsSFSbABRS6/w640-h160/Cancel_button_if.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> private void CancelButton_Click(object sender, RoutedEventArgs e)
{
if (tokenSource is not null)
{
tokenSource.Cancel();
}
}</code></pre>
<div><br /></div>
<div>
This second version also checks to make sure that the "tokenSource" field is
not null before calling the "Cancel" method. If the field is null, then
"Cancel" is not called:
</div>
<div>
<div><br /></div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMnwug-UDh28aGIr9DkX5TyeF4XtiCqd8oenyE-NAYnY4a0q7vldg9alPdX-87M_8mH8wA6TuzakT0rsDv6zjJi-pKhDKEi5hfT9_7txOUB1HvqpZODGO3buK1sOYVTeIUkuxSMtlBvTjhJzZ_p-3ZPMqYa62iYB9eDCR2YzCpNY0ULvf-A2M4RKtZ/s937/Cancel_button_nullconditional.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="138" data-original-width="937" height="94" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMnwug-UDh28aGIr9DkX5TyeF4XtiCqd8oenyE-NAYnY4a0q7vldg9alPdX-87M_8mH8wA6TuzakT0rsDv6zjJi-pKhDKEi5hfT9_7txOUB1HvqpZODGO3buK1sOYVTeIUkuxSMtlBvTjhJzZ_p-3ZPMqYa62iYB9eDCR2YzCpNY0ULvf-A2M4RKtZ/w640-h94/Cancel_button_nullconditional.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> private void CancelButton_Click(object sender, RoutedEventArgs e)
{
tokenSource?.Cancel();
}</code></pre>
<div><br /></div>
<div>
I say "almost equivalent" because the null conditional operator in the second
version gives us a little bit more -- it also helps with thread safety.
</div>
<div><br /></div>
<div>Let's look at these things in more detail.</div>
<div><br /></div>
<h2 style="text-align: left;">"Possibly Null" Warning</h2>
<div>
We are looking at the same code as the
<a href="https://jeremybytes.blogspot.com/2022/07/nullability-in-c-what-it-is-and-what-it.html">previous article</a>. This can be found in the
<a href="https://github.com/jeremybytes/nullability-in-csharp/blob/main/FinishedCode/UsingTask.UI/MainWindow.xaml.cs">"MainWindow.xaml.cs" file</a>
in the "UsingTask.UI" project of the GitHub repository.
</div>
<div><br /></div>
<div>
<i>As a reminder, the "StartingCode" folder has the starting state of the code
at the beginning of the first article in the series. The "FinishedCode" has
the completed code. The links in this article will point to the
"FinishedCode" folder.</i>
</div>
<div><br /></div>
<div>
Our code has a nullable "tokenSource" field defined in the class (from the
<a href="https://github.com/jeremybytes/nullability-in-csharp/blob/main/FinishedCode/UsingTask.UI/MainWindow.xaml.cs">"MainWindow.xaml.cs" file</a>
noted above):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbNlhqWWnvi5TdRBqbvrdZw6hogq1lQNNZe07mzSrRUNyGM8Jp3iTp91foDUyWcr-50ADQ0jgovErIox6kKXmikHp5GuRK-uTxKaUxBzIW5eDhRdHzBfhSgSjA16bKY3YPz9gbGqOC1Wxyb2_vlVHOsYZ2RfogQ2KN-IbJSfAy6WhF9il2eVOQbxHQ/s552/Nullable_field.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="207" data-original-width="552" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbNlhqWWnvi5TdRBqbvrdZw6hogq1lQNNZe07mzSrRUNyGM8Jp3iTp91foDUyWcr-50ADQ0jgovErIox6kKXmikHp5GuRK-uTxKaUxBzIW5eDhRdHzBfhSgSjA16bKY3YPz9gbGqOC1Wxyb2_vlVHOsYZ2RfogQ2KN-IbJSfAy6WhF9il2eVOQbxHQ/w400-h150/Nullable_field.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> CancellationTokenSource? tokenSource;
public MainWindow()
{
InitializeComponent();
}</code></pre>
<div><br /></div>
<div>
Since we have a "?" at the end of the field type "CancellationTokenSource", we
are indicating that this field can be null. (And since we are not assigning a
value to it in the constructor, it will be null initially).
</div>
<div><br /></div>
<div>This field is used in the Cancel button's event handler:</div>
<div>
<div><br /></div>
<div>
As we saw in the last article, in the code's initial state, we get a warning
when we access the "tokenSource" field:
</div>
<div><br /></div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQnL5iq8dK8yQiK9rVAUZLhqPAQFSQvl4-vd31BTfLAIhOWaUXbhLn7Y_A94ivC3j6uBiuhgie5ohPjFKJBdrMNUBk-HUJXnAB0zwmPxQeoEmJXNw3KAYsPQ7AJ1VE5o-V95CBvlHDDacEd2l2yyaK_ptD-ZwtezrJ_S4O4QaKY29WDR29hLEgQk3B/s937/Cancel_button_start.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="137" data-original-width="937" height="94" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiQnL5iq8dK8yQiK9rVAUZLhqPAQFSQvl4-vd31BTfLAIhOWaUXbhLn7Y_A94ivC3j6uBiuhgie5ohPjFKJBdrMNUBk-HUJXnAB0zwmPxQeoEmJXNw3KAYsPQ7AJ1VE5o-V95CBvlHDDacEd2l2yyaK_ptD-ZwtezrJ_S4O4QaKY29WDR29hLEgQk3B/w640-h94/Cancel_button_start.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> private void CancelButton_Click(object sender, RoutedEventArgs e)
{
<span style="background-color: #fcff01;">tokenSource</span>.Cancel();
}</code></pre>
<div><br /></div>
<div>
The warning tells us that the field may be null and result in a null reference
exception.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGcgBEYrv_OlZvy9lMVoqZxD1IMqHZmuEr-Ov1Nj_Yv6VxOjfmvfYOPbgb_A-E3WNGRnfoJDFMF8xBn9nPayFmslcFWxbB-_tj1I_Hn1BqQh0hGuz85GeWOe-QLccVOYnTu3XwFfAo2lzZsA1tQrtGXZ4r0_4t-3L9BH6_JO08idCB8q7rDLOQnSiz/s926/Cancel_button_warning.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="293" data-original-width="926" height="202" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGcgBEYrv_OlZvy9lMVoqZxD1IMqHZmuEr-Ov1Nj_Yv6VxOjfmvfYOPbgb_A-E3WNGRnfoJDFMF8xBn9nPayFmslcFWxbB-_tj1I_Hn1BqQh0hGuz85GeWOe-QLccVOYnTu3XwFfAo2lzZsA1tQrtGXZ4r0_4t-3L9BH6_JO08idCB8q7rDLOQnSiz/w640-h202/Cancel_button_warning.png" width="640" /></a>
</div>
<div><br /></div>
<code>CS8602: Dereference of a possibly null reference.</code>
<div><br /></div>
<div>
This gives us a warning that "tokenSource" may be null here. And if we call
the "Cancel" method on a null field, we will end up with a null reference
exception at runtime. (If you'd like to see this, it is shown in the
<a href="https://jeremybytes.blogspot.com/2022/07/nullability-in-c-what-it-is-and-what-it.html">previous article</a>.)
</div>
<div><br /></div>
<h2 style="text-align: left;">Checking for Nulls</h2>
<div>
The traditional way to check for nulls is to wrap the code in a guard clause.
This is often done with an "if" conditional:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ0B20sCrKMTmwuARVygheABRqaIZBqRr4tJdYM58nhRjpnVQ3TPDmYn4verCiKxuCCdUWwlDBvQ870EK4ASxHnipgWe6vy32FOxG4ExVPGSo-kXIDJDrB57UXNlqj2zmMz9s_IerDtAlmth1p2lbXfDdSMmcYZD2_pRiJNjXUK6EHbtsSFSbABRS6/s933/Cancel_button_if.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="233" data-original-width="933" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ0B20sCrKMTmwuARVygheABRqaIZBqRr4tJdYM58nhRjpnVQ3TPDmYn4verCiKxuCCdUWwlDBvQ870EK4ASxHnipgWe6vy32FOxG4ExVPGSo-kXIDJDrB57UXNlqj2zmMz9s_IerDtAlmth1p2lbXfDdSMmcYZD2_pRiJNjXUK6EHbtsSFSbABRS6/w640-h160/Cancel_button_if.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> private void CancelButton_Click(object sender, RoutedEventArgs e)
{
if (tokenSource is not null)
{
tokenSource.Cancel();
}
}</code></pre>
<div><br /></div>
<div>
The guard clause makes sure that the "tokenSource" field is not null before it
attempts to call the "Cancel" method.
</div>
<div><br /></div>
<div><b>***THREAD SAFETY WARNING***</b></div>
<div>
When we have code that references class-level fields (or other variables
external to a method), there is a possibility of running into problems with
threading. This is especially important to keep in mind since so much of the
code we write today is asynchronous.
</div>
<div><br /></div>
<div>So what can happen here?</div>
<div><br /></div>
<div>
There is a brief period of time between when we check for the null in the "if"
condition and the when we actually run the "Cancel" method. In this brief
period it is possible that another method has set the "tokenSource" property
to null.
</div>
<div><br /></div>
<div>
The result of this would be a null reference exception when the code tries to
call the "Cancel" method.
</div>
<div><br /></div>
<div>
To get around this, developers will often make a local copy of the field or
variable and act on that, particularly when dealing with delegates. No one
else can affect the state of the local variable inside the method, so the
"Cancel" method could be called with confidence.
</div>
<div><br /></div>
<div>
However, there is an easier way to deal with this thread safety issue: use the
null conditional operator.
</div>
<div><br /></div>
<h2 style="text-align: left;">Null Conditional Operator - ?.</h2>
<div>
The most common null conditional operator consists of a question mark and a
dot.
</div>
<div><br /></div>
<div>
<i>Note: The other null conditional operator uses a question mark with square
brackets - ?[]. This is used to access indexers, and we will look at this
briefly below. For more information, take a look at the Microsoft docs site
on null conditional operators: <a href="https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-">Member Access Operators</a>.</i>
</div>
<div><br /></div>
<div>Here is what this null conditional operator looks like in use:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMnwug-UDh28aGIr9DkX5TyeF4XtiCqd8oenyE-NAYnY4a0q7vldg9alPdX-87M_8mH8wA6TuzakT0rsDv6zjJi-pKhDKEi5hfT9_7txOUB1HvqpZODGO3buK1sOYVTeIUkuxSMtlBvTjhJzZ_p-3ZPMqYa62iYB9eDCR2YzCpNY0ULvf-A2M4RKtZ/s937/Cancel_button_nullconditional.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="138" data-original-width="937" height="94" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMnwug-UDh28aGIr9DkX5TyeF4XtiCqd8oenyE-NAYnY4a0q7vldg9alPdX-87M_8mH8wA6TuzakT0rsDv6zjJi-pKhDKEi5hfT9_7txOUB1HvqpZODGO3buK1sOYVTeIUkuxSMtlBvTjhJzZ_p-3ZPMqYa62iYB9eDCR2YzCpNY0ULvf-A2M4RKtZ/w640-h94/Cancel_button_nullconditional.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> private void CancelButton_Click(object sender, RoutedEventArgs e)
{
tokenSource?.Cancel();
}</code></pre>
<div><br /></div>
<div>
This has the same overall effect of the guard clause. At runtime, the
"tokenSource" field is checked for null. If the field is not null, then the
"Cancel" method is called normally.
</div>
<div><br /></div>
<div>
If the field <b>is</b> null, the operation stops. The "Cancel" method is
not called.
</div>
<div><br /></div>
<div>
But a shortened syntax is not all that we get with the null conditional
operator. We also get thread safety.
</div>
<div><br /></div>
<div><b>Thread Safety</b></div>
<div>
The language designers took threading into account when they created this
operator. From the developer's perspective, the null check and "Cancel" method
call happen as a single operation, meaning that there is no possibility that
something else would be able to set the "tokenSource" field to null in the
middle.
</div>
<div><br /></div>
<div>
I say "from the developer perspective" because the implementation is a little
more complex than that. If you're curious, you can always fire up ILDASM
(which is still included with the Visual Studio developer tools) and look at
the IL (intermediate language) that is generated by the compiler.
</div>
<div><br /></div>
<h2 style="text-align: left;">What About Return Values or Properties?</h2>
<div>
The next question is what happens if we use the null conditional operator to
call a method that returns a value or reads a property?
</div>
<div><br /></div>
<div>
The short answer is that if the null conditional operator encounters a null,
then the return value or property will be returned as "null".
</div>
<div><br /></div>
<div>
As an example, let's say that we only want to call the "Cancel" method if the
cancellation token is not already in a "canceled" state. Our "tokenSource"
field has a property called "IsCancellationRequested" that we can use to check
that.
</div>
<div><br /></div>
<div>
Here's what that code may look like (note: this is not in the final code
sample):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmIqkPEpMKE4NG7yzyIQ5iKo4QNmXQyqmlE6rIBhRSbtZoH2wFvkgHkkPqdiJ0eIjtiOJhmYvJQR7hDo50Z7p51Opiql4F8OlYiOEo0XDYLuyiAPhWxmgpLUgNJSijFZUS531SFzW3REDrl_lJNUOIskHZ0JuCkvqyqNtLGcX5S2zk5u-wgEKX93uC/s932/Property_withnull.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="231" data-original-width="932" height="158" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmIqkPEpMKE4NG7yzyIQ5iKo4QNmXQyqmlE6rIBhRSbtZoH2wFvkgHkkPqdiJ0eIjtiOJhmYvJQR7hDo50Z7p51Opiql4F8OlYiOEo0XDYLuyiAPhWxmgpLUgNJSijFZUS531SFzW3REDrl_lJNUOIskHZ0JuCkvqyqNtLGcX5S2zk5u-wgEKX93uC/w640-h158/Property_withnull.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> private void CancelButton_Click(object sender, RoutedEventArgs e)
{
if (!tokenSource?.IsCancellationRequested)
{
tokenSource?.Cancel();
}
}</code></pre>
<div><br /></div>
<div>
This uses an "if" statement to check the "IsCancellationRequested" property on
the "tokenSource" field.
</div>
<div><br /></div>
<div>But we have some red squigglies. Here is the error message:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7MALNNA9n2UMV34UEwe-900rXmm3bRZuNW9UVuKkjeqd8TdwKjBooBaHr1tp-PMsuH4WanmPxiN657CUhTk6yyYPhxIDoXZu6hO7fu14R-a3QLVLjNNXTid12C-hjGemHbfRMB6c_OH7C73OcKURqPUA6QThEIQXcEWt4VgWebO69zCIwNg4wvFxF/s1312/Property_withnull_error.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="308" data-original-width="1312" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7MALNNA9n2UMV34UEwe-900rXmm3bRZuNW9UVuKkjeqd8TdwKjBooBaHr1tp-PMsuH4WanmPxiN657CUhTk6yyYPhxIDoXZu6hO7fu14R-a3QLVLjNNXTid12C-hjGemHbfRMB6c_OH7C73OcKURqPUA6QThEIQXcEWt4VgWebO69zCIwNg4wvFxF/w640-h150/Property_withnull_error.png" width="640" /></a>
</div>
<div><br /></div>
<code>CS0266: Cannot implicitly convert type 'bool?' to 'bool'. An explicit
conversion exists (are you missing a cast?)</code>
<div><br /></div>
<div>
This tells us that "if" needs a non-nullable Boolean value ("bool"). But since
we use the null conditional operator, when we try to read
"IsCancellationRequested", we may get a "null" back (this is noted with the
nullable Boolean ("bool?") in the message).
</div>
<div><br /></div>
<div>
We won't go through the trouble of fixing this code here. This sample is to
show what happens when we use the null conditional operator on something that
returns a value (either by calling a method or reading a property). In those
cases, we may get a "null" returned.
</div>
<div><br /></div>
<div><b>Null Coalescing Operator - ??</b></div>
<div>
In a future article, we will take a look at the null coalescing operator that
lets us return a default value if we run across a null. This can help us fix
things like the scenario above and eliminate possible null values.
</div>
<div><br /></div>
<h2 style="text-align: left;">Null Conditional Operator - ?[]</h2>
<div>
The null conditional operator that consists of a question mark and a set of
square brackets is used for indexers on objects. The behavior is very similar
to using the ?. operator to access a property.
</div>
<div><br /></div>
<div>
For example, let's consider the following code (note: this is not part of the
sample code on GitHub):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJf6USYdhzk_EGFHlDT6DkF3fI8XmhoChTO0swISCrvX_ENKwPuLxRcBM9-fGyNRfNWyT2q3gwNaFdRY8B8WCswXBeq5LNixCJJKJALR6VVCMADDIwS6haiUfK6qqjhPo4O-WFg5DpWJqjN2zI7t_w2WKT_0Hmb1cj--r5NQYRAoU0B5o9XrNpYMc6/s490/Indexer.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="77" data-original-width="490" height="63" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJf6USYdhzk_EGFHlDT6DkF3fI8XmhoChTO0swISCrvX_ENKwPuLxRcBM9-fGyNRfNWyT2q3gwNaFdRY8B8WCswXBeq5LNixCJJKJALR6VVCMADDIwS6haiUfK6qqjhPo4O-WFg5DpWJqjN2zI7t_w2WKT_0Hmb1cj--r5NQYRAoU0B5o9XrNpYMc6/w400-h63/Indexer.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> List<Person>? people = null;
Person? firstPerson = people?[0];</code></pre>
<div><br /></div>
<div>
In this code, "people" is a nullable list of "Person" objects (and we set it
to null). When we try to index into the "people" list, we put a question mark
between "people" and the square brackets with the indexer.
</div>
<div><br /></div>
<div>
If "people" is null, the indexer <b>is not</b> accessed, and so we do not get
a null reference exception here. The "firstPerson" variable would then be
assigned "null".
</div>
<div><br /></div>
<div>
If "people" is not null, the indexer <b>is</b> accessed, and the first
item in the collection will be returned.
</div>
<div><br /></div>
<div>
<i>As a side note, if the list is empty, we will get an "Index Out of Range"
exception at runtime. But this is something we normally have to deal with
regardless of whether nullability is enabled.</i>
</div>
<div><br /></div>
<div>
So we can use the ?[] null conditional operator to index into a nullable
object. And this works similarly to using the ?. null conditional operator to
access a property on a nullable object. If the object is null, then we get a
null back. If the object is not null, then we get the item at the index or the
value of the property.
</div>
<div><br /></div>
<h2 style="text-align: left;">Wrap Up</h2>
<div>
The null conditional operators can help us in 2 ways. First they can shorten
our code by eliminating the need for a separate guard clause that specifically
checks for "null". Secondly, they give us thread safety during the null check,
so we do not need to worry about the possibility of a "null" sneaking into our
code in a multi-threaded or async scenario.
</div>
<div><br /></div>
<div>
Enabling nullability gives us the warnings we need to eliminate possible nulls
in our code. And the null operators (including the null conditional operators)
can help us in dealing with those warnings.
</div>
<div><br /></div>
<div>
In the next 2 articles, we will look at the null forgiving operator as well as
the null coalescing operators. These give us additional tools when dealing
with possible nulls in our code. Be sure to check back for more.
</div>
<div><br /></div>
<div>
<b>Next Article:</b> <a href="https://jeremybytes.blogspot.com/2022/07/null-forgiving-operator-in-c.html">Null Forgiving Operator in C# - !</a>
</div>
<div><br /></div>
<div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-47633723121758145152022-07-15T13:01:00.011-07:002023-02-22T12:02:58.225-08:00Nullability in C# - What It Is and What It Is Not<div>
Starting with .NET 6, new projects have nullable reference types enabled by
default. It is easy to get confused on exactly what that means, particularly
when migrating existing projects. Today, we'll take a look at what nullability
is and what it isn't. In future articles, we'll look at the null operators in
C# (null conditional, null coalescing, and null forgiving) -- these are all
various combinations of "?", "!", and ".".
</div>
<div><br /></div>
<h4 style="text-align: left;">Articles</h4>
<ul style="text-align: left;">
<li>
Nullability in C# - What It Is and What It Is Not (<b>this article</b>)
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-conditional-operators-in-c-and.html">Null Conditional Operators in C# - ?. and ?[]</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-forgiving-operator-in-c.html">Null Forgiving Operator in C# - !</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2022/07/null-coalescing-operators-in-c-and.html">Null Coalescing Operators in C# - ?? and ??=</a>
</li>
<li>
<a href="https://jeremybytes.blogspot.com/2023/02/c-var-with-reference-types-is-always.html">
C# "var" with a Reference Type is Always Nullable
</a>
</li>
</ul>
<div>
<i>The source code for this article series can be found on GitHub: <a href="https://github.com/jeremybytes/nullability-in-csharp">https://github.com/jeremybytes/nullability-in-csharp</a>.</i>
</div>
<div><br /></div>
<h2 style="text-align: left;">The Short Version</h2>
<div><b>Nullability Is</b>:</div>
<div>
<ul style="text-align: left;">
<li>
A way to get <i>compile-time</i> warnings about possible null
references
</li>
<li>
A way to make the intent of your code more clear to other developers
</li>
</ul>
</div>
<div><b>Nullability Is NOT</b>:</div>
<div>
<ul style="text-align: left;">
<li>A way to prevent null reference exceptions at runtime</li>
<li>
A way to prevent someone from passing a null to your method or assigning a
null to an object
</li>
</ul>
</div>
<div><br /></div>
<div>Read on for details and examples.</div>
<div><br /></div>
<h2 style="text-align: left;">Getting a Null Reference Exception at Runtime</h2>
<div>
Before looking at what nullability gives us, let's take a look at a project
that does <b>not</b> have nullability enabled. This can be found at the GitHub
repository noted above: <a href="https://github.com/jeremybytes/nullability-in-csharp">https://github.com/jeremybytes/nullability-in-csharp</a>. (See the README file of on the repository for information on how to run the
application yourself.)
</div>
<div><br /></div>
<div>
The "StartingCode" folder contains a set of projects where nullability is
<b>not</b> enabled. Here is a screenshot of the running application (the
"UsingTask.UI" project in the solution):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8GUr4DRz8BsMc2ef-ZHM7jsRJPC9SRocGpm-j2VF_8f1YdDumrR8yo0JO9Ppjtf0w8ckiMQOGYHBB_e2SM-k4ExnZKxUHfq3zDPTmyZFJ3fci26nOvCluooUv3VfBaFQSuzjIhuZY5YGktQ_Aylv1-5o1RquQeiyQz9QIBHuIBZ2fVJtAxFquRy8z/s932/Running_application.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="655" data-original-width="932" height="281" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8GUr4DRz8BsMc2ef-ZHM7jsRJPC9SRocGpm-j2VF_8f1YdDumrR8yo0JO9Ppjtf0w8ckiMQOGYHBB_e2SM-k4ExnZKxUHfq3zDPTmyZFJ3fci26nOvCluooUv3VfBaFQSuzjIhuZY5YGktQ_Aylv1-5o1RquQeiyQz9QIBHuIBZ2fVJtAxFquRy8z/w400-h281/Running_application.png" width="400" /></a>
</div>
<br />
<div><br /></div>
<div><br /></div>
<div>
And here is the code hooked up to to the "Cancel" button at the bottom
(specifically from the
<a href="https://github.com/jeremybytes/nullability-in-csharp/blob/main/StartingCode/UsingTask.UI/MainWindow.xaml.cs">MainWindow.xaml.cs file</a>
in the "UsingTask.UI" project):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-LFF8B3b-ZZV4k2nd3P3ZLQOk6BY7Q7Bcxhb9dBLWNPv6RnvvecpfjvYFlGphC0fHAs-gANUaLtxqqUVaPLlP3-kQUmZuYMBtJ9Oz8k5bija2yQLTO2n2Yc74abmfJGPNSMACOsPFYqowHMEbPmmOrMPT5PiUAUlNKdPoGWdxGtO2FJtifwf24-Ms/s937/Cancel_button_code1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="138" data-original-width="937" height="94" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-LFF8B3b-ZZV4k2nd3P3ZLQOk6BY7Q7Bcxhb9dBLWNPv6RnvvecpfjvYFlGphC0fHAs-gANUaLtxqqUVaPLlP3-kQUmZuYMBtJ9Oz8k5bija2yQLTO2n2Yc74abmfJGPNSMACOsPFYqowHMEbPmmOrMPT5PiUAUlNKdPoGWdxGtO2FJtifwf24-Ms/w640-h94/Cancel_button_code1.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> private void CancelButton_Click(object sender, RoutedEventArgs e)
{
tokenSource.Cancel();
}</code></pre>
<div><br /></div>
<div>
The problem with the code is that the "tokenSource" field in this code may be
null. (We won't go into the details of why that may be true; if you want more
information, you can look at the resources about Task and Cancellation
here: <a href="https://github.com/jeremybytes/using-task-dotnet6">https://github.com/jeremybytes/using-task-dotnet6</a>.)
</div>
<div><br /></div>
<div>
If we run the application and then immediately click the "Cancel" button
(without clicking either of the other buttons first), we get a runtime error
-- a Null Reference Exception:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiliqs5XqI03aCZ5w4yaoLC0zibJ9B9mnlRpUg77-FG6A0QgF2WWcWFK2gmvKOq6tGAEuqLb33Mgf9fy5G7u8aWs-59pNRfnYvuzLZityK9VajQGPMFvLt18XeXbC2sGqHhyVzuA91gv6_GHo1GwhnCZ3EIkpmcjT4o_QX9H9K7fgVJyTrcvtm6GrER/s917/Null_reference_exception.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="421" data-original-width="917" height="294" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiliqs5XqI03aCZ5w4yaoLC0zibJ9B9mnlRpUg77-FG6A0QgF2WWcWFK2gmvKOq6tGAEuqLb33Mgf9fy5G7u8aWs-59pNRfnYvuzLZityK9VajQGPMFvLt18XeXbC2sGqHhyVzuA91gv6_GHo1GwhnCZ3EIkpmcjT4o_QX9H9K7fgVJyTrcvtm6GrER/w640-h294/Null_reference_exception.png" width="640" /></a>
</div>
<br />
<div>
<code>System.NullReferenceException: "Object reference not set to an instance of
an object."</code>
</div>
<div><br /></div>
<div>
This is because we are trying to call the "Cancel" method on a null
tokenSource.
</div>
<div><br /></div>
<div>
Nullability and nullable reference types are there to help us prevent these
types of errors. So let's enable nullability and see what we help we get (and
what help we do not get).
</div>
<div><br /></div>
<h2 style="text-align: left;">Enabling Nullable Reference Types</h2>
<div>
As mentioned above, nullability is enabled by default when you create a
project with .NET 6. Nullability can also be enabled by editing the .csproj
file for projects that are upgraded from .NET 5 or .NET Core.
</div>
<div><br /></div>
<div>
To enable nullable reference types, set the "Nullable" property to enable in
the .csproj file. Here is an excerpt from the
<a href="https://github.com/jeremybytes/nullability-in-csharp/blob/main/FinishedCode/UsingTask.UI/UsingTask.UI.csproj">UsingTask.UI.csproj file</a>
(note the code in the "StartingCode" folder has this commented out; the
"FinishedCode" has it uncommented):
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBs8ss3X2op8L7vewhKPKaAODRzx2lb9bSedtDetEgaqHnh18dOCE9H5beeFaOnk_m_tCbBZ6vcXKemsWaJL0oHza7BfGNwUiwPn2n-bokdZPS0SnagV-NrlMHJZ6lj-cm4FB6SZOCoCsjm9OD8EVB1CqK1phx2LOKSZYMmy5O54nFqeAon_7JQht8/s757/Nullable_enable.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="240" data-original-width="757" height="202" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBs8ss3X2op8L7vewhKPKaAODRzx2lb9bSedtDetEgaqHnh18dOCE9H5beeFaOnk_m_tCbBZ6vcXKemsWaJL0oHza7BfGNwUiwPn2n-bokdZPS0SnagV-NrlMHJZ6lj-cm4FB6SZOCoCsjm9OD8EVB1CqK1phx2LOKSZYMmy5O54nFqeAon_7JQht8/w640-h202/Nullable_enable.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> <PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UseWPF>true</UseWPF>
</PropertyGroup></code></pre>
<div><br /></div>
<div>
<i>Note: I also made this change to the
<a href="https://github.com/jeremybytes/nullability-in-csharp/blob/main/FinishedCode/UsingTask.Library/UsingTask.Library.csproj">UsingTask.Library.csproj file</a>
(but we will not look at this project until a later article).</i>
</div>
<div><br /></div>
<div>
Now that nullability is enabled, let's take a look at what this means for the
project.
</div>
<div><br /></div>
<div>
With this property set, all reference types (whether they used as fields,
properties, method arguments, return types, or in other ways) are assumed to
be non-nullable. To make a reference type nullable, we need to explicitly
state that (and we will see how in just a bit).
</div>
<div><br /></div>
<h3 style="text-align: left;">
Nullability Is: A way to get compile-time warnings about possible null
references
</h3>
<div>
The first thing we can do is see what messages we get. If we re-build the
solution, some green squigglies will show up to alert us to possible problems.
For example, there is now a warning on on the constructor for the MainWindow
class:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWo_8uOR7IjbIdJegprTi9clPuHE07jzg9AW_CzlXxvOGeaplODT94s4BprocTWiZ4HOdjwJObB6N2l5iJmjRQ8XGvjFBIutNkkYFA40pAy67MK4NZT8R37ZB9tWsJUSl4bQvZDNwpyyCfRXcxi7UOS-IfxicOhl1MyHeC9GGNNn5wa8k_ti1RXA9n/s531/Constructor_information.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="212" data-original-width="531" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWo_8uOR7IjbIdJegprTi9clPuHE07jzg9AW_CzlXxvOGeaplODT94s4BprocTWiZ4HOdjwJObB6N2l5iJmjRQ8XGvjFBIutNkkYFA40pAy67MK4NZT8R37ZB9tWsJUSl4bQvZDNwpyyCfRXcxi7UOS-IfxicOhl1MyHeC9GGNNn5wa8k_ti1RXA9n/w400-h160/Constructor_information.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> CancellationTokenSource tokenSource;
public <span style="background-color: #fcff01;">MainWindow</span>()
{
InitializeComponent();
}</code></pre>
<div><br /></div>
<div>If we hover over the warning, we can get the details:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaIU-ubutjrSSEOPzodpAd8Js0FJ7wLwauWtafr1FeiL4dFlqaKU7kL725FlHUll7AMCNzqLInY2nrWev_dfd7o6S2Bf-XZdp_qTEuhr98nrpM76x2BS4lLzYo2LJnzuSBSCWiVZIkZY7cRERL7jqyJl_TE33a7w-PGPtUtifb2k25z7W-v53xbXkg/s1202/Constructor_more_information.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="270" data-original-width="1202" height="144" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaIU-ubutjrSSEOPzodpAd8Js0FJ7wLwauWtafr1FeiL4dFlqaKU7kL725FlHUll7AMCNzqLInY2nrWev_dfd7o6S2Bf-XZdp_qTEuhr98nrpM76x2BS4lLzYo2LJnzuSBSCWiVZIkZY7cRERL7jqyJl_TE33a7w-PGPtUtifb2k25z7W-v53xbXkg/w640-h144/Constructor_more_information.png" width="640" /></a>
</div>
<br /><code>CS8618: Non-nullable field 'tokenSource' must contain a non-null value when
exiting constructor. Consider declaring the field as nullable.</code>
<div><br /></div>
<div>
This message tells us that the "tokenSource" field will be null when the
constructor exits (and so the field will be null).
</div>
<div><br /></div>
<div>
Another way is to open the "Error List" in Visual Studio to see each of the
warnings listed.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZN6uMUfLmXG1t86S-Ku9bpkMPxF_I6tMqlivXw8CKppMY9WzJOEFioNkvLkc-FnidO4gIPPLKbRRSkHLuHs9AUfbq22NWE6E-N3__B6dkIPIzXnq-TSn2ZMOts9HHVxtRCcCRzwuL0nGTQ7deLAzxqdfrYk5YZ4O9j_P3orGevrtuThFDJLlZckLF/s962/Error_list.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="115" data-original-width="962" height="76" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZN6uMUfLmXG1t86S-Ku9bpkMPxF_I6tMqlivXw8CKppMY9WzJOEFioNkvLkc-FnidO4gIPPLKbRRSkHLuHs9AUfbq22NWE6E-N3__B6dkIPIzXnq-TSn2ZMOts9HHVxtRCcCRzwuL0nGTQ7deLAzxqdfrYk5YZ4O9j_P3orGevrtuThFDJLlZckLF/w640-h76/Error_list.png" width="640" /></a>
</div>
<br />
<div>
This includes the message above as well as 2 others (we will explore these in
later articles).
</div>
<div><br /></div>
<div>
<i>So Nullability <b>is</b> a way to get compile-time warnings about possible
null references.</i>
</div>
<div><br /></div>
<h3 style="text-align: left;">
Nullability Is NOT: A way to prevent null reference exceptions at runtime
</h3>
<div>
Although we get the warnings (and that helps us), this does not prevent us
from building and running the application.
</div>
<div><br /></div>
<div>
If we build and run the application again, and then click the "Cancel" button,
we get the same Null Reference Exception that we got before:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiliqs5XqI03aCZ5w4yaoLC0zibJ9B9mnlRpUg77-FG6A0QgF2WWcWFK2gmvKOq6tGAEuqLb33Mgf9fy5G7u8aWs-59pNRfnYvuzLZityK9VajQGPMFvLt18XeXbC2sGqHhyVzuA91gv6_GHo1GwhnCZ3EIkpmcjT4o_QX9H9K7fgVJyTrcvtm6GrER/s917/Null_reference_exception.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="421" data-original-width="917" height="294" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiliqs5XqI03aCZ5w4yaoLC0zibJ9B9mnlRpUg77-FG6A0QgF2WWcWFK2gmvKOq6tGAEuqLb33Mgf9fy5G7u8aWs-59pNRfnYvuzLZityK9VajQGPMFvLt18XeXbC2sGqHhyVzuA91gv6_GHo1GwhnCZ3EIkpmcjT4o_QX9H9K7fgVJyTrcvtm6GrER/w640-h294/Null_reference_exception.png" width="640" /></a>
</div>
<br />
<div>
<code>System.NullReferenceException: "Object reference not set to an instance of
an object."</code>
</div>
<div><br /></div>
<div>
Even though the "tokenSource" field is non-nullable, this is not enforced at
runtime. Since we do not assign a value to the "tokenSource" in the
constructor, it is null when we use it in the Cancel button's event handler.
</div>
<div><br /></div>
<div>
<i>So Nullability <b>is not</b> a way to prevent null reference exceptions at
runtime.</i>
</div>
<div><br /></div>
<div>
This is important to keep in mind when we are working with nullability. It is
helpful to us at compile-time, but it does not have a runtime effect.
</div>
<div><br /></div>
<div>
<h3 style="text-align: left;">
Nullability Is NOT: A way to prevent someone from passing a null to your
method or assigning a null to an object
</h3>
</div>
<div>
As we saw above, these are compiler warnings (and those warnings are useful).
But they do not prevent someone from assigning "null" to an object or passing
"null" as an argument to a method.
</div>
<div><br /></div>
<div>
As a very contrived example, we can assign a "null" to the "tokenSource" field
in the constructor:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMv7OriOR55YxTmsuToFuKmjI5s0wz9h3vpQ_uz6i5xExGAutcKuw-ZxOP-2VvubVo7s2a94Y2RBcOGX1Q1aRtST3W5s8KuA_yxq5PnBqsgRRcPLUUkLXVFmyvu-XeSMD4rysAfS2au5h9xSFyvv6ZAN4pOwNDG12f0osub5lN274_KLNTo4AHr6rH/s530/Constructor_warning1.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="237" data-original-width="530" height="179" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMv7OriOR55YxTmsuToFuKmjI5s0wz9h3vpQ_uz6i5xExGAutcKuw-ZxOP-2VvubVo7s2a94Y2RBcOGX1Q1aRtST3W5s8KuA_yxq5PnBqsgRRcPLUUkLXVFmyvu-XeSMD4rysAfS2au5h9xSFyvv6ZAN4pOwNDG12f0osub5lN274_KLNTo4AHr6rH/w400-h179/Constructor_warning1.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> CancellationTokenSource tokenSource;
public MainWindow()
{
InitializeComponent();
tokenSource = null;
}</code></pre>
<div><br /></div>
<div>We do get a warning:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisdGBrtYK22M-YuIySzp2yPKnOgqweHsTsjP-OyStZp4k4sV-clwUMH_oAboZXieyulzeea1wLji-3eN4S_J_9fxPyH84F_JtLfWh0teHpt84H3JGTOTPuPv5oPR4WgXT0_Q_FnzGWgUWA6JOol_LuM43lJcgRaxNx-EZ-sV2LRA3nlCJ3dEYoU7IH/s1012/Constructor_warning2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="367" data-original-width="1012" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisdGBrtYK22M-YuIySzp2yPKnOgqweHsTsjP-OyStZp4k4sV-clwUMH_oAboZXieyulzeea1wLji-3eN4S_J_9fxPyH84F_JtLfWh0teHpt84H3JGTOTPuPv5oPR4WgXT0_Q_FnzGWgUWA6JOol_LuM43lJcgRaxNx-EZ-sV2LRA3nlCJ3dEYoU7IH/w640-h232/Constructor_warning2.png" width="640" /></a>
</div>
<br />
<code>CS8625: Cannot convert null literal to non-nullable reference type.</code>
<div><br /></div>
<div>
And the warning is useful. If we have our compiler set to fail on warnings
(which is used by many teams), then it would stop this code from compiling.
But there is nothing that forces another developer to have "fail on warnings"
turned on. The code is still buildable and runnable.
</div>
<div><br /></div>
<div>
More subtly, if we are building a library that is used by another project,
that project could pass a null to one of our library methods (even if we have
it specified as non-nullable). So it is still very important that we check for
nulls in our code. We'll dive into this a little deeper in a subsequent
article.
</div>
<div><br /></div>
<div>
<div>
<i>So Nullability <b>is not</b> a way to prevent someone from passing a null
to your method or assigning a null to an object.</i>
</div>
<div><br /></div>
</div>
<h3 style="text-align: left;">
Nullability Is: A way to make the intent of your code more clear to other
developers
</h3>
<div>
One key feature of nullable reference types is that it can make the intent of
your code more clear. When we have nullability enabled in our code, all
reference types are non-nullable by default. If we want to have a field or
variable that is nullable, we need to mark it as such using "?".
</div>
<div><br /></div>
<div>
The "tokenSource" field should be nullable. To let other developers (and the
compiler) know about this, we add a question mark at the end of the type when
we declare the field:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3YJxgWlNlPy37BvK78SrLbsOsB7Wyd5ef-cw5r-_lhsAg_CpGy1eVsvJP3MQv4Pgk-vdGnaulXgDM4SvDdfOhOk3G1IsgDXaoWTEWXrpDMB_KNtv0ePuZihk1Rghfag9l8pDyN-9z9Mn5zLGVnWzlYdVzcRVj5FdvwPFUthzmZJLR0UWfKrr5QFw8/s552/Constructor_nowarning.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="207" data-original-width="552" height="150" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3YJxgWlNlPy37BvK78SrLbsOsB7Wyd5ef-cw5r-_lhsAg_CpGy1eVsvJP3MQv4Pgk-vdGnaulXgDM4SvDdfOhOk3G1IsgDXaoWTEWXrpDMB_KNtv0ePuZihk1Rghfag9l8pDyN-9z9Mn5zLGVnWzlYdVzcRVj5FdvwPFUthzmZJLR0UWfKrr5QFw8/w400-h150/Constructor_nowarning.png" width="400" /></a>
</div>
<div><br /></div>
<pre><code> <span style="background-color: #fcff01;">CancellationTokenSource?</span> tokenSource;
public MainWindow()
{
InitializeComponent();
}</code></pre>
<div><br /></div>
<div>
This means that the "tokenSource" field is allowed to be null, and the warning
that we got on the constructor is now gone.
</div>
<div><br /></div>
<div>
But now that the "tokenSource" field is nullable, we get a different warning
in the Cancel button's event handler:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_uILze3cOPGpZZ4kles8S1JNXRYhzrzHDy-OTTSPU-wkM1WfJugtQsygLwCkoKWgpdp-E4kf29ErrTa6zP-dr1Grm_CZXyRyWFKN_LQHY-So-7Ps-QxPQ8R8YYoNdCxs1TDDym_vrYrs1uqNP4glVUGuyNP05V4-sSF9-bZtRfO-gUTfQcyQa63Z2/s937/Cancel_button_warning.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="137" data-original-width="937" height="94" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg_uILze3cOPGpZZ4kles8S1JNXRYhzrzHDy-OTTSPU-wkM1WfJugtQsygLwCkoKWgpdp-E4kf29ErrTa6zP-dr1Grm_CZXyRyWFKN_LQHY-So-7Ps-QxPQ8R8YYoNdCxs1TDDym_vrYrs1uqNP4glVUGuyNP05V4-sSF9-bZtRfO-gUTfQcyQa63Z2/w640-h94/Cancel_button_warning.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> private void CancelButton_Click(object sender, RoutedEventArgs e)
{
<span style="background-color: #fcff01;">tokenSource</span>.Cancel();
}</code></pre>
<div><br /></div>
<div>Here are the message details:</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjujFLxdVciRrOcPD-_JDtIXXH-IM6KXOxB_xUnUbP6YDrT1ZtMhXD-hdhqyGsLyNgUbnl4PJzAy_HcpGYwXDyKLbvCxS9KYEQ2_cZ26u6qRKXntmLi8cLn72BNVoTSCtggzQoee-oU7cSa6iH0DtXBU3QynheFaHRhAGysEzAsn9ri07sHEymhXHMe/s926/Cancel_button_warning2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="293" data-original-width="926" height="202" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjujFLxdVciRrOcPD-_JDtIXXH-IM6KXOxB_xUnUbP6YDrT1ZtMhXD-hdhqyGsLyNgUbnl4PJzAy_HcpGYwXDyKLbvCxS9KYEQ2_cZ26u6qRKXntmLi8cLn72BNVoTSCtggzQoee-oU7cSa6iH0DtXBU3QynheFaHRhAGysEzAsn9ri07sHEymhXHMe/w640-h202/Cancel_button_warning2.png" width="640" /></a>
</div>
<br />
<div><br /></div>
<code>'tokenSource' may be null here.</code>
<div>
<code>CS8602: Dereference of a possibly null reference.</code>
</div>
<div><br /></div>
<div>
This tells us that we have a potential for a null reference exception at
runtime (and we have seen that happen several times already).
</div>
<div><br /></div>
<div>
When we come across a message like this, we should have a guard clause that
does a null check. The way that we used to do this is to wrap the code in an
"if" statement:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3alhmkNvRgDD2bDlq_iw7MB6Hg7TllrneDoUiG1QsW3un6iZgZ38ALKMEYWJSMbjr_PE7FDUdFA_CGJt4XFsOyCOaYpaZsTbMy_DYSnDhsIdWytEEjJX6wf7SD834TdjhGGUZHBL2KQlPY8ny8lhDzc7WfYsWyaQBOE4uYBYy0y9QQ6B6CvN0rLwL/s933/Cancel_button_code2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="233" data-original-width="933" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3alhmkNvRgDD2bDlq_iw7MB6Hg7TllrneDoUiG1QsW3un6iZgZ38ALKMEYWJSMbjr_PE7FDUdFA_CGJt4XFsOyCOaYpaZsTbMy_DYSnDhsIdWytEEjJX6wf7SD834TdjhGGUZHBL2KQlPY8ny8lhDzc7WfYsWyaQBOE4uYBYy0y9QQ6B6CvN0rLwL/w640-h160/Cancel_button_code2.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> private void CancelButton_Click(object sender, RoutedEventArgs e)
{
if (tokenSource is not null)
{
tokenSource.Cancel();
}
}</code></pre>
<div><br /></div>
<div>
This code makes sure that "tokenSource" is not null. If it is null, then it
skips over the code and does nothing. Otherwise, it will run the "Cancel"
method. This prevents the null reference exception at runtime.
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfTM-g844lno75EBWn2_-3JlSHzlirQkGIy4UHVVPuYCJh3LUK9psROVz6v6bIidb5KDY6Zp8dAzN-uwsTQ7s0qW5vHm84iidyLTeQSoFLTeNZn157jEKrBwQCqFFNsgL7ZZ_hkvdWQCU3fTuXv4-Odb_pWBabdha_gOHpL5JH2YHoX02cB808aub8/s932/Running_application2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="655" data-original-width="932" height="281" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfTM-g844lno75EBWn2_-3JlSHzlirQkGIy4UHVVPuYCJh3LUK9psROVz6v6bIidb5KDY6Zp8dAzN-uwsTQ7s0qW5vHm84iidyLTeQSoFLTeNZn157jEKrBwQCqFFNsgL7ZZ_hkvdWQCU3fTuXv4-Odb_pWBabdha_gOHpL5JH2YHoX02cB808aub8/w400-h281/Running_application2.png" width="400" /></a>
</div>
<br />
<div><br /></div>
<div>
Even if we click the "Cancel" button immediately after starting the
application, we do not get a null reference exception. This is because we have
a guard clause to prevent that.
</div>
<div><br /></div>
<div>
The code above works, but there is an easier way of doing this with the null
conditional operator:
</div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4vYy356l0ZBK9jkLpqBLJPev2kzXbIfHZpzVcvpsOhBf08htS8cqG0ESkG0lrDsm0RVMjtBaGLAXH6CpPJM_R36HrtXLmksLos77KWXnEzEwS5XLTubL5ofsl2isClyMNWxMmX07DLBQ62myTPdVmGoMn-r93oxtfcCKNE9doMriYDjcg5gmGzTzL/s937/Cancel_button_code3.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="138" data-original-width="937" height="94" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi4vYy356l0ZBK9jkLpqBLJPev2kzXbIfHZpzVcvpsOhBf08htS8cqG0ESkG0lrDsm0RVMjtBaGLAXH6CpPJM_R36HrtXLmksLos77KWXnEzEwS5XLTubL5ofsl2isClyMNWxMmX07DLBQ62myTPdVmGoMn-r93oxtfcCKNE9doMriYDjcg5gmGzTzL/w640-h94/Cancel_button_code3.png" width="640" /></a>
</div>
<div><br /></div>
<pre><code> private void CancelButton_Click(object sender, RoutedEventArgs e)
{
tokenSource?.Cancel();
}</code></pre>
<div><br /></div>
<div>
We won't go into the details of the null conditional operator right now; that
is the subject of the next article.
</div>
<div><br /></div>
<h2 style="text-align: left;">Wrap Up</h2>
<div>
So we have seen that nullability and nullable reference types can be very
useful for sharing the intent of our code. It can also help us find potential
null reference exceptions in our application. But it does not stop us from
compiling, and it does not stop null reference exceptions from happening at
runtime.
</div>
<div><br /></div>
<div><b>Nullability Is</b>:</div>
<div>
<ul style="text-align: left;">
<li>
A way to get <i>compile-time</i> warnings about possible null
references
</li>
<li>
A way to make the intent of your code more clear to other developers
</li>
</ul>
</div>
<div><b>Nullability Is NOT</b>:</div>
<div>
<ul style="text-align: left;">
<li>A way to prevent null reference exceptions at runtime</li>
<li>
A way to prevent someone from passing a null to your method or assigning a
null to an object
</li>
</ul>
</div>
<div>
The usefulness is very good, and it can save us a lot of time hunting down
bugs in our code. But we do need to be careful not to rely on it too heavily.
</div>
<div><br /></div>
<div>
<b>Next Article:</b> <a href="https://jeremybytes.blogspot.com/2022/07/null-conditional-operators-in-c-and.html">Null Conditional Operators in C# - ?. and ?[]</a>
</div>
<div><br /></div>
<div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com1tag:blogger.com,1999:blog-5359546512544809971.post-26051488289691641882022-04-13T08:58:00.000-07:002022-04-13T08:58:16.788-07:00Returning HTTP 204 (No Content) from .NET Minimal API<div>
I recently converted some ASP.NET web api projects from using
controllers to using minimal apis. And I ran into a weirdness. If you return
"null" from a controller method, then the response is HTTP 204 (No Content),
but if you return "null" from a minimal api, the response is HTTP 200 (OK)
with the string "null" as the body.
</div>
<br />
<div>The short version:</div>
<div>
<b><span style="font-size: large;"></span></b>
<blockquote>
<b><span style="font-size: large;">To return HTTP 204 from a minimal API method, use
"Results.NoContent()" as your return value.</span></b>
</blockquote>
</div>
<div>This also means that if you want to return actual content, you will need to
wrap that in something like "Results.Json(your_content)".</div>
<div><br /></div>
<div>
If you're interested, read on for my experience. I'm sure that I'll get
"Jeremy, you're doing it wrong" responses, but that does not invalidate my
learning path which may help someone else.
</div>
<div><br /></div>
<div>
I'll start out by saying that web api is not my area of expertise. I primarily
use it so that I can spin up test services to get fake data for my
applications.
</div><div><br /></div>
<h2 style="text-align: left;">Controller Behavior</h2>
<div>
For my api, there is an endpoint that allows you to get an individual "Person"
item by specifying an ID.
</div>
<div><br /></div>
<div>Sample endpoint:</div>
<pre><code> http://localhost:9874/people/2</code></pre>
<div><br /></div>
<div>Sample output:</div>
<div><br /></div>
<code> {"id":2,"givenName":"Dylan","familyName":"Hunt","startDate":"2000-10-02T00:00:00-07:00","rating":8,"formatString":""}</code>
<div><br /></div>
<div><br /></div><div>This comes from a controller method that looks like this:</div>
<pre><code> [HttpGet("{id}")]
public async Task<Person?> GetPerson(int id)
{
return await _provider.GetPerson(id);
}</code></pre>
<div><br /></div>
<div>
The return value from this method is a nullable Person. If the ID is found,
then the Person is returned, otherwise, "null" is fine.
</div>
<div><br /></div>
<div>
However, the result to the caller is not "null", instead it is an HTTP 204 (No
Content). This is appropriate, and we can see some Fiddler results that show
this.
</div>
<div><br /></div>
<div><b>Valid Record</b></div>
<div>
Fiddler "Raw" result with call to
"<code>http://localhost:9874/people/2</code>":
</div>
<pre><code>HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 13 Apr 2022 14:26:50 GMT
Server: Kestrel
Content-Length: 117
{"id":2,"givenName":"Dylan","familyName":"Hunt","startDate":"2000-10-02T00:00:00-07:00","rating":8,"formatString":""}</code></pre>
<div>This shows the HTTP 200 (OK) with the JSON results at the bottom.</div>
<div><br /></div>
<div><b>Invalid Record</b></div>
<div>
Fiddler result with call to "<code>http://localhost:9874/people/20</code>":
</div>
<pre><code>HTTP/1.1 204 No Content
Content-Length: 0
Date: Wed, 13 Apr 2022 14:27:00 GMT
Server: Kestrel</code></pre>
<div>
This shows that if we use id=20 (which does not exist), then we get the HTTP
204 (No Content) that we expect for this api.
</div><div><br /></div>
<h2 style="text-align: left;">Minimal API Default Template</h2>
<div>
Since this service had only a few endpoints (3), I converted them to use
minimal apis in .NET 6. To be honest, I did not do a lot of research before
attempting this. I primarily looked at the default template for the minimal api
project and went from there.
</div>
<div><br /></div>
<div>
I used the following command to generate the project from the template:
</div>
<pre><code> dotnet new webapi -minimal --no-https</code></pre>
<div><br /></div>
<div>The sample endpoint has weather forecast data:</div>
<pre><code> app.MapGet("/weatherforecast", () =>
{
var forecast = Enumerable.Range(1, 5).Select(index =>
new WeatherForecast
(
DateTime.Now.AddDays(index),
Random.Shared.Next(-20, 55),
summaries[Random.Shared.Next(summaries.Length)]
))
.ToArray();
return forecast;
})
.WithName("GetWeatherForecast");</code></pre>
<div>
This shows a "/weatherforecast" endpoint that does not take parameters, and
returns the resulting object (an array of "WeatherForecast") directly.</div><div><br /></div>
<h2 style="text-align: left;">My First Pass</h2>
<div>
On my first pass at this, I attempted something similar. Here's the endpoint
that I created:
</div>
<pre><code> app.MapGet("/people/{id}", async (int id, IPeopleProvider provider) =>
{
return await provider.GetPerson(id);
})
.WithName("GetPerson");</code></pre>
<div>
This is a bit more complex since the endpoint has a parameter, and the method
has some dependency injection.
</div>
<div><br /></div>
<div>
The basics of the lambda expression parameters: the first parameter ("id") is
mapped to the "{id}" parameter of the endpoint. The "provider" parameter comes
from the dependency injection container. The controller version also uses dependency injection (although the "IPeopleProvider" dependency is mapped through the constructor rather than the method).
</div>
<div><br /></div>
<div>
The content of the minimal api method is exactly the same as the content of
the controller method (the return type is "Task<Person?>" in both
cases).
</div>
<div><br /></div>
<div>However, the behavior is different.</div>
<div><br /></div>
<div>Let's look at some output.</div>
<div><br /></div>
<div><b>Valid Record</b></div>
<div>
Fiddler "Raw" result with call to
"<code>http://localhost:9874/people/2</code>":
</div>
<pre><code>HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 13 Apr 2022 14:27:46 GMT
Server: Kestrel
Content-Length: 117
{"id":2,"givenName":"Dylan","familyName":"Hunt","startDate":"2000-10-02T00:00:00-07:00","rating":8,"formatString":""}</code></pre>
<div>
This is the same result that we had with the controller method: HTTP 200 (OK)
with the JSON results at the bottom.
</div>
<div><br /></div>
<div><b>Invalid Record</b></div>
<div>
Fiddler result with call to "<code>http://localhost:9874/people/20</code>":
</div>
<pre><code>HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 13 Apr 2022 14:27:49 GMT
Server: Kestrel
Content-Length: 4
null</code></pre>
<div>
This is where things get strange. The result is HTTP 200 (OK) and the body of
the response is "null" -- the string "null".
</div>
<div><br /></div>
<div>This looks even stranger when you run this in a browser. </div>
<div><br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnxKIH2UhkF2kZg0ufl9IFGG0U5h9k_S8I8EmtHyxP1dSEWR1v5fgdgi2WN3Tt-Qrma6sGXyWvXDgGMRxsqQmf6pwPshrPXTvfkg62RWoKAzOPNXCr_LXF5CC3zbZA8gRJQ71yDpzJekKqVwNh3hB1HGhhx3Dzb5LSv3F-Lh2ZfM-IRgEwVr8WMksA/s405/null_output.png" style="margin-left: 1em; margin-right: 1em;"><img alt="Web browser open tab with the string "null" shown as the output." border="0" data-original-height="182" data-original-width="405" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnxKIH2UhkF2kZg0ufl9IFGG0U5h9k_S8I8EmtHyxP1dSEWR1v5fgdgi2WN3Tt-Qrma6sGXyWvXDgGMRxsqQmf6pwPshrPXTvfkg62RWoKAzOPNXCr_LXF5CC3zbZA8gRJQ71yDpzJekKqVwNh3hB1HGhhx3Dzb5LSv3F-Lh2ZfM-IRgEwVr8WMksA/w400-h180/null_output.png" width="400" /></a>
</div>
<br />
<div><br /></div>
<div>
This is not the behavior that I want (and it actually broke some code that was
looking for the HTTP 204 status code).</div><div><br /></div>
<h2 style="text-align: left;">Returning HTTP 204 (No Content)</h2>
<div>
I did a little bit of searching and found some other folks who were getting the behavior. In the end I found I needed to take more control over the
response from the api method.
</div>
<div><br /></div>
<div>Here's the method that I ended up with:</div>
<code>
<pre> app.MapGet("/people/{id}", async (int id, IPeopleProvider provider) =>
{
var person = await provider.GetPerson(id);
return person switch
{
null => Results.NoContent(),
_ => Results.Json(person)
};
})
.WithName("GetPerson");</pre>
</code>
<div>
This is a bit different. In this case, I get the "person" back from the
"GetPerson" method on the provider. This will either be populated or null (as
we saw before).
</div>
<div><br /></div>
<div>
Then if the "person" variable is null, I explicitly return
"Results.NoContent()".
</div>
<div><br /></div>
<div>
If the "person" variable is not null, then I take the value and wrap it in
"Results.Json()".
</div>
<div><br /></div>
<div><i>
Note: We cannot just return the "person" variable like we did earlier because
we need the signature of the lambda expression to be consistent. Since the
"null" path returns a "Results", we need the happy path to also return a
"Results".
</i></div>
<div><br /></div>
<div>This gets us back to where we were before:</div>
<div><br /></div>
<div><b>Valid Record</b></div>
<div>
Fiddler "Raw" result with call to
"<code>http://localhost:9874/people/2</code>":
</div>
<pre><code>HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 13 Apr 2022 15:27:32 GMT
Server: Kestrel
Content-Length: 117
{"id":2,"givenName":"Dylan","familyName":"Hunt","startDate":"2000-10-02T00:00:00-07:00","rating":8,"formatString":""}</code></pre>
<div>This shows the HTTP 200 (OK) with the JSON results at the bottom.</div>
<div><br /></div>
<div><b>Invalid Record</b></div>
<div>
Fiddler result with call to "<code>http://localhost:9874/people/20</code>":
</div>
<pre><code>HTTP/1.1 204 No Content
Date: Wed, 13 Apr 2022 15:27:34 GMT
Server: Kestrel</code></pre>
<div>
This shows that if we use ID=20 (which does not exist), then we get the HTTP
204 (No Content) that we expect for this api. (Yay!)</div><div><br /></div><h2 style="text-align: left;">Why I Wrote This</h2><div>The reason I wrote this article is two-fold. The first is that if I have run into an issue, there are probably other folks who have run into it as well. More info is good.</div><div><br /></div><div>The second is that I'm a bit frustrated with the messaging that comes from Microsoft. I should be used to it by now. I have been seeing for years how "easy" something is in the demo. But when it comes to building something a bit more real, things get harder.</div><div><br /></div><div>In particular, the default template is a bit misleading. Yes, it will work for a lot of scenarios, but since the behavior here is specifically different from using controllers (which many people will be converting from), it would be really nice to have a "this is different" marker somewhere.</div><div><br /></div><div>The other thing is about "minimal api" tutorial (<a href="https://docs.microsoft.com/en-us/aspnet/core/tutorials/min-web-api?view=aspnetcore-6.0&tabs=visual-studio">Tutorial: Create a minimal web API with ASP.NET Core</a>). Even though it does show using "Results" in the code, the only instruction is "copy this code into your project". There is no explanation of the code or why you might need it. (In my opinion, it's not much of a tutorial if it doesn't explain why you are doing something.)</div><div><br /></div><div>Anyway, I hope that this will be helpful to someone out there. Feel free to share your thoughts.</div><div><br /></div><h2 style="text-align: left;">Wrap Up</h2><div>I seem to be having quite a few "who moved my cheese?" moments as the .NET framework and C# language continue to evolve. I really don't mind my cheese being somewhere else, but I would *really* appreciate a sign that says:</div><div><b><span style="font-size: large;"></span></b><blockquote><b><span style="font-size: large;">"Your cheese is no longer here. Here's where you can find it."</span></b></blockquote></div><div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-5809689518155839112021-12-20T09:43:00.003-08:002021-12-21T06:50:32.637-08:00Cancelling IAsyncEnumerable in C#<div>
IAsyncEnumerable combines the power of IEnumerable (which lets us "foreach"
through items) with the joys of async code (that we can "await"). Like many
async methods, we can pass in a CancellationToken to short-circuit the
process. But because of the way that we use IAsyncEnumerable, passing in a
cancellation token is a bit different than we are used to.
</div>
<div><br /></div>
<div><b>Short Version:</b></div>
<blockquote>
<span style="font-size: medium;"><b>The "WithCancellation" extension method on IAsyncEnumerable lets us pass
in a cancellation token.</b></span>
</blockquote>
<div>
Let's take a quick look at how to use IAsyncEnumerable, and then we'll look at
cancellation.
</div>
<div><br /></div>
<h3 style="text-align: left;">Using IAsyncEnumerable</h3>
<div>
I started using IAsyncEnumerable when I exploring channels in C#.
Here's an example method that uses "await foreach" with an IAsyncEnumerable
(taken from the
<a href="https://github.com/jeremybytes/csharp-channels-presentation/blob/main/people-demo/PeopleViewer/Program.cs">Program.cs file</a>
of this GitHub repository:
<a href="https://github.com/jeremybytes/csharp-channels-presentation">https://github.com/jeremybytes/csharp-channels-presentation</a>):
</div>
<pre><code> private static async Task ShowData(ChannelReader<Person> reader)
{
await foreach(Person person in reader.ReadAllAsync())
{
DisplayPerson(person);
};
}</code></pre>
<div>
The ChannelReader type has a "ReadAllAsync" method that returns an
IAsyncEnumerable. What this means is that someone else can be writing to the
channel while we read from it. The IAsyncEnumerable means that if there is not
another item ready to read, we can wait for it. So, we can pull things off the
channel as they are added, even if there are delays between each item getting
added.
</div>
<div><br /></div>
<div>
Waiting for items can cause a problem, and that's where the "async" part comes
in. Since this is asynchronous, we can "await" each item. This gives us the
advantages that we are used to with "await", meaning that we are not blocking
processes and threads while we wait.
</div>
<div><br /></div>
<div>
By using "await foreach" on an IAsyncEnumerable, we get this combined
functionality: getting the next item in the enumeration and waiting
asynchronously if the next item isn't ready yet.
</div><div><br /></div><div><i>For more information on channels, check the repository for list of articles and a recorded presentation: <a href="https://github.com/jeremybytes/csharp-channels-presentation">https://github.com/jeremybytes/csharp-channels-presentation</a>.</i></div>
<div><br /></div>
<h3 style="text-align: left;">The IAsyncEnumerable Interface</h3>
<div>
Things got a little more interesting when I was building my own classes that implement IAsyncEnumerable. The interface itself only has one method:
GetAsyncEnumerator. An implementation could look something like this (we'll
call this our "Processor" since it will process some custom data for us):
</div>
<pre><code> public IAsyncEnumerator<int> GetAsyncEnumerator(
CancellationToken cancellationToken = default)
{
while (...)
{
// interesting async stuff here
yield return nextValue;
}
}
</code></pre>
<div>
Like with IEnumerable, we can use "yield return" to return the next value from
the enumeration. Unlike IEnumerable, we can also put asynchronous code in
here, whether it's an asynchronous service call or waiting for a record to
finish a complex process. (That goes in the "// interesting async stuff here"
section; we won't look at that today.)
</div>
<div><br /></div>
<h3 style="text-align: left;">CancellationToken Parameter</h3>
<div>
An interesting thing about the GetAsyncEnumerable method is that it has a
CancellationToken parameter. Notice that the the CancellationToken has a
"default" value set. This means that the cancellation token is optional. If we do not pass in a token, the code will still work.
</div>
<div><br /></div>
<div>
If we wanted to check the cancellation token in the code. This could look
something like this:
</div>
<pre><code> public async IAsyncEnumerator<int> GetAsyncEnumerator(
CancellationToken cancellationToken = default)
{
while (...)
{
<span style="background-color: #fcff01;">cancellationToken.ThrowIfCancellationRequested()</span>;
// interesting stuff here
yield return nextValue;
}
}
</code></pre>
<div>
Each time through the "while" loop, the cancellation token will be checked. If
cancellation is requested, then this will throw an OperationCanceledException.
</div>
<div><br /></div>
<h3 style="text-align: left;">Passing a Cancellation Token</h3>
<div>
The next part is where things get interesting. How do we actually pass a
cancellation token to the IAsyncEnumerable? If we are using the "Processor"
that we created above. That call could look something like this.
</div>
<pre><code> await foreach (int currentItem in processor)
{
DisplayItem(currentItem);
}</code></pre>
<div>
When we use "foreach", we do not call the "GetAsyncEnumerable" directly. That
also means that we cannot pass in a cancellation token directly.
</div>
<div><br /></div>
<div>
But there is an extension method available on IAsyncEnumerable that helps us
out: <b>WithCancellation</b>.
</div>
<div><br /></div>
<div>Here's that same foreach loop with a cancellation token passed in:</div>
<pre><code> await foreach (int currentItem in processor<span style="background-color: #fcff01;">.WithCancellation(tokenSource.Token)</span>)
{
DisplayItem(currentItem);
}</code></pre>
<div>
This assumes that we have a CancellationTokenSource (called "tokenSource")
elsewhere in our code.
</div>
<div><br /></div>
<div>
<i>For more information on Cancellation, you can refer to
<a href="https://jeremybytes.blogspot.com/2015/01/task-and-await-basic-cancellation.html">Task and Await: Basic Cancellation</a>. The article was written a while back. Updated code samples (.NET 6) are
available here: <a href="https://github.com/jeremybytes/using-task-dotnet6">https://github.com/jeremybytes/using-task-dotnet6</a>.</i>
</div>
<div><br /></div>
<h3 style="text-align: left;">ConfigureAwait</h3>
<div>
As a side note, there is another extension method on IAsyncEnumerable:
ConfigureAwait. This lets us use "ConfigureAwait(false)" in areas that we need
it. ConfigureAwait isn't needed in the ASP.NET world anymore, but it can still
be useful if we are doing desktop or other types of programming.
</div>
<div><br /></div>
<h3 style="text-align: left;">Wrap Up</h3>
<div>
IAsyncEnumerable gives us some pretty interesting abilities. I've been
exploring it quite a bit lately. In some code comparisons, I was able to move
async code into a library that made using it quite a bit easier. Once that sample
code is ready, I'll be sharing it on GitHub.
</div>
<div><br /></div>
<div>
Until then, keep exploring. Sometimes the answers are not obvious. It's okay
if it takes some time to figure things out.
</div>
<div><br /></div>
<div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com0tag:blogger.com,1999:blog-5359546512544809971.post-55884613048117148842021-09-30T01:08:00.006-07:002021-09-30T21:32:38.273-07:00Coding Practice: Learning Rust with Fibonacci NumbersIn my exploration of Rust, I built an application that calculates Fibonacci
numbers (this was a suggestion from the end of
<a href="https://doc.rust-lang.org/book/ch03-05-control-flow.html">Chapter 3 of The Rust Programming Language</a> by Steve Klabnik and Carol Nichols).
<div><br /></div>
<div>It helped me learn a bit more about the language and environment.</div>
<ul>
<li><code>for</code> loops</li>
<li>Statements vs. expressions</li>
<li>Function returns (expressions)</li>
<li><code>checked_add</code> to prevent overflow</li>
<li>Option enum (returned from <code>checked_add</code>)</li>
<li>Pattern matching on Option</li>
<li>Result enum (to return error rather than panic)</li>
<li><code>.expect</code> with Result</li>
<li>Pattern matching on Result</li>
</ul>
<div>So let's walk through this project.</div>
<div><br /></div>
<div>
<i>The code is available on GitHub: <a href="https://github.com/jeremybytes/fibonacci-rust">https://github.com/jeremybytes/fibonacci-rust</a>, and branches are set up for each step along the way. We will only be
looking at the "main.rs" file in each branch, so all of the links will be
directly to this file.</i>
</div>
<div><br /></div>
<h3 style="text-align: left;">Fibonacci Numbers</h3>
<div>
The task is to calculate the nth Fibonacci number. The Fibonacci sequence is
made by adding the 2 previous number in the sequence. So the sequence starts:
1, 1, 2, 3, 5, 8, 13, 21, 34. The 7th Fibonacci number (13) is the sum of the
previous 2 numbers (5 and 8).
</div>
<div><br /></div>
<div>
For our application, we will create a function to generate the nth Fibonacci
number based on an input parameter. We'll call this function with multiple
values and output the results.
</div>
<div><br /></div>
<h3 style="text-align: left;">Step 1: Creating the Project</h3>
<div>
<b>Branch</b>:
<a href="https://github.com/jeremybytes/fibonacci-rust/blob/01-creation/src/main.rs">01-creation</a>
</div>
<div>
The first step is to create the project. We can do this by typing the
following in a terminal:
</div>
<pre><code> cargo new fib</code></pre>
<div>
This will create a new folder called "fib" along with the Rust project file, a
"src" folder to hold the code, and a "main.rs" file (in src) which is where we
will be putting our code.
</div>
<div><br /></div>
<div>The "main.rs" file has placeholder code:</div>
<pre><code> fn main() {
println!("Hello, world!");
}</code></pre>
<div>
But we can use "cargo run" to make sure that everything is working in our
environment.
</div>
<pre><code> C:\rustlang\fib> cargo run
Compiling fib v0.1.0 (C:\rustlang\fib)
Finished dev [unoptimized + debuginfo] target(s) in 0.63s
Running `target\debug\fib.exe`
Hello, world!</code></pre>
<div>
<i>Going forward, I'll just show the application output (without the compiling
and running output).</i>
</div>
<div><br /></div>
<h3 style="text-align: left;">Step 2: Basic Fibonacci</h3>
<div>
<b>Branch</b>:
<a href="https://github.com/jeremybytes/fibonacci-rust/blob/02-basic/src/main.rs">02-basic</a>
</div>
<div>
Now that we have the shell, let's create a function to return a Fibonacci
number. Here's the completed function:
</div>
<pre><code> fn fib(n: u8) -> u64 {
let mut prev: u64 = 0;
let mut curr: u64 = 1;
for _ in 1..n {
let next = prev + curr;
prev = curr;
curr = next;
}
curr
}</code></pre>
<div>There are several interesting bits here. Let's walk through them.</div>
<div><br /></div>
<div><b>Declaring a Function</b></div>
<div>Let's start with the function declaration:</div>
<pre><code> fn fib(n: u8) -> u64 {
}</code></pre>
<div>
The "fn" denotes that this is a function. "fib" is the function name. "n: u8"
declares a parameter called "n" that is an unsigned 8-bit integer. And the
"u64" after the arrow declares that this function returns an unsigned 64-bit
integer.
</div>
<div><br /></div>
<div>
When declaring parameters and return values for functions, the types are
required. Rust does use type inference in some places (as we'll see), but
function declarations need to have explicit types.
</div>
<div><br /></div>
<div><b>Declaring Variables</b></div>
<div>Next, we have some variables declared and assigned:</div>
<pre><code> let mut prev: u64 = 0;
let mut curr: u64 = 1;</code></pre>
<div>"let" declares a variable.</div>
<div><br /></div>
<div>
By default, variables are immutable. This means that once we assign a value,
we cannot change it. For these variables, we use "mut" to denote that they are
mutable. So we will be able to change the values later.
</div>
<div><br /></div>
<div>
The variable names are "prev" and "curr". These will hold the "previous
number" and the "current number" in the sequence.
</div>
<div><br /></div>
<div>
The ": u64" declares these as unsigned 64-bit integer values. Fibonacci
numbers tend to overflow very quickly, so I used a fairly large integer type.
</div>
<div><br /></div>
<div>Finally, we assign initial values of 0 and 1, respectively.</div>
<div><br /></div>
<div><b>Looping with "for"</b></div>
<div>
There are several ways to handle the loop required to calculate the Fibonacci
number. I opted for a "for" loop:
</div>
<pre><code> for _ in 1..n {
}</code></pre>
<div>
"1..n" represents a range from 1 to the value of the incoming function
argument. So if the argument is "3", this represents the range: 1, 2, 3.
</div>
<div><br /></div>
<div>
The "for" statement will loop once for each value in the range. In this case
the "_" denotes that we are not using the actual range value inside the loop.
All we really need here is to run the loop 3 times. All of the calculation is
done inside the loop itself.
</div>
<div><br /></div>
<div><b>Implicit Typing</b></div>
<div>Inside the "for" loop we do our calculations:</div>
<pre><code> let next = prev + curr;
prev = curr;
curr = next;</code></pre>
<div>
This creates a new variable called "next" inside the loop and assigns it the
sum of "prev" and "curr". A couple of things to note. First, this variable is
immutable (so we do not have the "mut" keyword). The value is assigned here
and then it is not changed. Second, the "next" variable is implicitly typed.
Instead of having a type declaration, it is set based on what is assigned to
it. Since we are assigning the sum of two u64 values, "next" will also be a
u64.
</div>
<div><br /></div>
<div>
The next two lines update the "prev" and "curr" values. We needed to mark them
as mutable when we declared them so that we could update them here.
</div>
<div><br /></div>
<div>
<i>This is a fairly naïve way of calculating Fibonacci numbers. If you'd like
to see more details on how the calculation works, you can take a look at
this article:
<a href="https://jeremybytes.blogspot.com/2017/04/tdding-into-fibonacci-sequence-in-c.html">TDDing into a Fibonacci Sequence with C#</a>.</i>
</div>
<div><br /></div>
<div><b>Statements vs. Expressions</b></div>
<div>The last line of the function is a bit interesting:</div>
<pre><code> curr</code></pre>
<div>This returns the current value ("curr") from the function.</div>
<div><br /></div>
<div>
Rust does not use a "return" keyword to return a value. Instead, the last
expression in a function is what is returned. (As a side note, this is similar
to how F# works.)
</div>
<div><br /></div>
<div>
What's the difference between a statement and an expression? A statement does
some type of work; an expression returns a value.
</div>
<div><br /></div>
<div>
In Rust, a statement ends with a semi-colon, and an expression does not end
with a semi-colon. To make things more interesting, these are often combined.
</div>
<div><br /></div>
<div>Let's take a look back at a line of code:</div>
<pre><code> let next = prev + curr;</code></pre>
<div>
Overall, this is a statement: it declares and assigns a value to a variable
called "next". And it ends with a semi-colon.
</div>
<pre><code> prev + curr</code></pre>
<div>
"prev + curr" is an expression that returns the result of adding 2 values. So
we really have a statement that includes an expression. (We can technically
break this down further, but we won't do that here.)
</div>
<div><br /></div>
<div>
So, let's get back to the return value of the function. The "fib" function
returns a u64 value. The last expression in the function is:
</div>
<pre><code> curr</code></pre>
<div>
It is important to note that this line does <b>not</b> end with a semi-colon.
Because of this, the value of the "curr" variable (which is a u64) is returned
for this function.
</div>
<div><br /></div>
<div>
<i>Because of my coding history, I'm used to putting semi-colons at the end of
lines. So I'm sure that I'll mess this up many times before I get used to
it. If you get an error that says a function is returning "()" instead of a
particular type, it probably means that there's a semi-colon at the end of
the expression you meant to return.</i>
</div>
<div><br /></div>
<div>Here's the full function:</div>
<pre><code> fn fib(n: u8) -> u64 {
let mut prev: u64 = 0;
let mut curr: u64 = 1;
for _ in 1..n {
let next = prev + curr;
prev = curr;
curr = next;
}
curr
}</code></pre>
<div><b>Using the "fib" Function</b></div>
<div>
Now that we have a function that returns a Fibonacci number, it's time to
update the "main" function to use it.
</div>
<pre><code> fn main() {
println!("Fibonacci 1st = {}", fib(1));
println!("Fibonacci 2nd = {}", fib(2));
println!("Fibonacci 3rd = {}", fib(3));
println!("Fibonacci 4th = {}", fib(4));
println!("Fibonacci 5th = {}", fib(5));
}</code></pre>
<div>
This uses the "println!" macro to output a string to the standard output. On
each line, the set of curly braces represents a placeholder in the string. So
in the first statement, the curly braces will be replaced by the value that
comes back from calling "fib(1)".
</div>
<div><br /></div>
<div>So let's run and check the output:</div>
<pre><code> Fibonacci 1st = 1
Fibonacci 2nd = 1
Fibonacci 3rd = 2
Fibonacci 4th = 3
Fibonacci 5th = 5</code></pre>
<div>It works!</div>
<div><br /></div>
<div>Well, it mostly works. We'll see a shortcoming in a bit.</div>
<div><br /></div>
<h3 style="text-align: left;">Step 3: Testing More Values</h3>
<div>
<b>Branch</b>:
<a href="https://github.com/jeremybytes/fibonacci-rust/blob/03-mainloop/src/main.rs">03-mainloop</a>
</div>
<div>
Before looking at where we have a problem in the "fib" function, let's make it
easier to test for different values. For this, we'll add an array of numbers
to test, and then loop through them.
</div>
<div><br /></div>
<div>Here's an updated "main" function:</div>
<pre><code> fn main() {
let nths = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
for nth in nths {
println!("Fibonacci {} = {}", nth, fib(nth));
}
}</code></pre>
<div><b>Creating an Array</b></div>
<div>
The first line sets up an array called "nths" and initializes it with the
values of 1 through 10. I use this name because our original task was to
calculate the "nth" Fibonacci number. This is a collection of all the ones we
want to calculate.
</div>
<div><br /></div>
<div>
We're using type inference to let the compiler pick the type for "nths". In
this case, it determines that it is an array with 10 elements of type u8. It
decides on u8 because the values are used as arguments for the "fib" function,
and that takes a u8.
</div>
<div><br /></div>
<div>
As an interesting note, if you comment out the "println!" statement, the
"nths" variable is an array with 10 elements of type i32 (a signed 32-bit
integer). This is the default integer type.
</div>
<div><br /></div>
<div>
Type inference works as long as it can be determined at compile time. If it
cannot be determined at compile time, then an explicit type needs to be added.
</div>
<div><br /></div>
<div><b>Another "for" Loop</b></div>
<div>
We use a "for" loop to go through the array. Instead of discarding the value
from the "for" loop (like we did above), we capture it in the "nth" variable.
</div>
<div><br /></div>
<div>
Inside the loop, we have a "println!" with 2 placeholders, one for the loop
value and one for the result of the "fib" function.
</div>
<div><br /></div>
<div>Here's what that output looks like:</div>
<pre><code> Fibonacci 1 = 1
Fibonacci 2 = 1
Fibonacci 3 = 2
Fibonacci 4 = 3
Fibonacci 5 = 5
Fibonacci 6 = 8
Fibonacci 7 = 13
Fibonacci 8 = 21
Fibonacci 9 = 34
Fibonacci 10 = 55</code></pre>
<div>And now we can more easily test values by adding to the array.</div>
<div><br /></div>
<div><b>Overflow!</b></div>
<div>
As I noted at the beginning, Fibonacci sequences tend to overflow pretty
quickly (they increase the value by half for each item). We can see this by
adding a "100" to our array.
</div>
<pre><code> let nths = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100];</code></pre>
<div>Here's the output when we run with these values:</div>
<pre><code> Fibonacci 1 = 1
Fibonacci 2 = 1
Fibonacci 3 = 2
Fibonacci 4 = 3
Fibonacci 5 = 5
Fibonacci 6 = 8
Fibonacci 7 = 13
Fibonacci 8 = 21
Fibonacci 9 = 34
Fibonacci 10 = 55
thread 'main' panicked at 'attempt to add with overflow', src\main.rs:13:20
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\fib.exe` (exit code: 101)</code></pre>
<div>
This creates a "panic" in our application, and it exits with an error.
</div>
<div><br /></div>
<div>
With a little experimentation, we will find that the 93rd Fibonacci number is
fine, but the 94th will overflow the u64 value.
</div>
<div><br /></div>
<h3 style="text-align: left;">Checking for Overflow</h3>
<div>
<b>Branch</b>:
<a href="https://github.com/jeremybytes/fibonacci-rust/blob/04-checkedadd/src/main.rs">04-checkedadd</a>
</div>
<div>
Now we can work on fixing the overflow. Our problem is with this line:
</div>
<pre><code> let next = prev + curr;</code></pre>
<div>
If "prev" and "curr" are near the upper limits of the u64 range, then adding
them together will go past that upper limit.
</div>
<div><br /></div>
<div>
Some other languages will "wrap" the value (starting over again at 0). Rust
will generate an error instead (in the form of a panic). If you do want to
wrap the value, Rust does offer a "wrapped_add" function that does just that.
</div>
<div><br /></div>
<div>
But we do not want to wrap, we would like to catch the error and give our
users a better experience.
</div>
<div><br /></div>
<div><b>checked_add</b></div>
<div>
Instead of using the default "+" operator, we can use the "checked_add"
function. Here is that code:
</div>
<pre><code> let result = prev.checked_add(curr);</code></pre>
<div>
"checked_add" does not panic if the value overflows. This is because it uses
the Option enum.
</div>
<div><br /></div>
<div><b>Option Enum</b></div>
<div>
The Option enum lets us return either a valid value or no value.
"Some<T>" is used if there is a valid value, otherwise "None" is used.
</div>
<div><br /></div>
<div>
For example, let's say that "prev" is 1 and "curr" is 2. The "result" would be
"Some(3)".
</div>
<div><br /></div>
<div>
If "prev" and "curr" are big enough to cause an overflow when added together,
then "result" would be "None".
</div>
<div><br /></div>
<div><b>Pattern Matching</b></div>
<div>
The great thing about having an Option as a return type is that we can use
pattern matching with it.
</div>
<div><br /></div>
<div>Here is the inside of the updated "for" loop:</div>
<pre><code> let result = prev.checked_add(curr);
match result {
Some(next) => {
prev = curr;
curr = next;
}
None => {
curr = 0;
break;
}
}</code></pre>
<div>The "match" keyword sets up the pattern matching for us.</div>
<div><br /></div>
<div>
The first "arm" has "Some(next)" as the pattern. The "next" part lets us
assign a name to the value that we can use inside the block. In this case,
"next" will hold the same value that it did in the earlier version ("prev" +
"curr"), so inside the block, we can assign the "prev" and "curr" values like
we did before.
</div>
<div><br /></div>
<div>
The second "arm" has "None" as the pattern. This will be used if there is an
overflow. If there is an overflow, then we set the "curr" variable to 0 and
then break out of the "for" loop.
</div>
<div><br /></div>
<div>Here is the updated "fib" function:</div>
<pre><code> fn fib(n: u8) -> u64 {
let mut prev: u64 = 0;
let mut curr: u64 = 1;
for _ in 1..n {
let result = prev.checked_add(curr);
match result {
Some(next) => {
prev = curr;
curr = next;
}
None => {
curr = 0;
break;
}
}
}
curr
}</code></pre>
<div>Here's an updated array to test valid values and overflow values:</div>
<pre><code> let nths = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 90, 91, 92, 93, 94, 95, 96];</code></pre>
<div>And here's the output:</div>
<pre><code> Fibonacci 1 = 1
Fibonacci 2 = 1
Fibonacci 3 = 2
Fibonacci 4 = 3
Fibonacci 5 = 5
Fibonacci 6 = 8
Fibonacci 7 = 13
Fibonacci 8 = 21
Fibonacci 9 = 34
Fibonacci 10 = 55
Fibonacci 90 = 2880067194370816120
Fibonacci 91 = 4660046610375530309
Fibonacci 92 = 7540113804746346429
Fibonacci 93 = 12200160415121876738
Fibonacci 94 = 0
Fibonacci 95 = 0
Fibonacci 96 = 0</code></pre>
<div>
Setting "curr" to 0 is not a great way to handle our error state. For now, it
is fine because it gets rid of the panic, and our application keeps running.
</div>
<div><br /></div>
<div>Next up, we'll work on getting a real error that we can handle.</div>
<div><br /></div>
<h3 style="text-align: left;">Using the Result Enum</h3>
<div>
<b>Branch</b>:
<a href="https://github.com/jeremybytes/fibonacci-rust/blob/05-result/src/main.rs">05-result</a>
</div>
<div>
In the last article, I wrote a bit about my first impressions of error
handling in Rust: <a href="https://jeremybytes.blogspot.com/2021/09/initial-impressions-of-rust.html">Initial Impressions of Rust</a>. A big part of that involves the Result enum.
</div>
<div><br /></div>
<div>
Similar to the Option enum, the Result enum represents exclusive states. For
Result, the options are "Ok" and "Err". Each of these can have their own type.
</div>
<div><br /></div>
<div>
To update our "fib" function to return a Result, we'll need to make 2 updates.
</div>
<div><br /></div>
<div><b>Updating the Function Declaration</b></div>
<div>
First, we'll need to update the signature of the function to return a Result.
Here's the new signature:
</div>
<pre><code> fn fib(n: u8) -> Result<u64, &'static str> {
}</code></pre>
<div>
The "Result" enum has 2 generic type parameters. The first represents the type
for the "Ok" value; the second represents the type for the "Err".
</div>
<div><br /></div>
<div>In this case, the "Ok" will be a u64.</div>
<div><br /></div>
<div>
The "Err" is a bit more confusing. We want to return a string, but if we try
to use just "str", we get an error that the compiler cannot determine the size
at compile time. And as we've seen, Rust needs to be able to determine things
at compile time.
</div>
<div><br /></div>
<div>
Instead of using "str", we can use "&str" to use the address of a string
(Rust does use pointers; we won't talk too much about them today). The address
is a fixed size, so that gets rid of the previous error. But we get a new
error that there is a "missing lifetime specifier".
</div><div><br /></div><div><i>UPDATE Technical Note: '&str' is a string slice. This allows the Err to borrow the value of the string without taking ownership of it. (I've learned more about ownership since I wrote this article. It's pretty interesting.)</i></div>
<div><br /></div>
<div>
The good news is that the error also gives you a hint to consider using the
"static" lifetime with an example.
</div>
<div><br /></div>
<div>
<i>I'm using Visual Studio Code with the Rust extension, so I get these errors
and hints in the editor. But these same messages show up if you build using
"cargo build".</i>
</div>
<div><br /></div>
<div><b>Returning a Result</b></div>
<div>
Now that we've updated the function signature, we need to actually return a
Result. We can do this with some pattern matching.
</div>
<div><br /></div>
<div>Replace the previous expression at the end of the function:</div>
<pre><code> curr</code></pre>
<div>with a "match":</div>
<pre><code> match curr == 0 {
false => Ok(curr),
true => Err("Calculation overflow")
}</code></pre>
<div>
This looks at the value of the "curr" variable and compares it to 0. (Again,
this isn't the best way to handle this, but we'll fix it a bit later).
</div>
<div><br /></div>
<div>
If "curr" is not 0 (meaning there is a valid value), then we hit the "false"
arm and return an "Ok" with the value.
</div>
<div><br /></div>
<div>
If "curr" is 0 (meaning there was an overflow), then we hit the "true" arm and
return an "Err" with an appropriate message.
</div>
<div><br /></div>
<div>Here's the updated "fib" function:</div>
<pre><code> fn fib(n: u8) -> Result<u64, &'static str> {
let mut prev: u64 = 0;
let mut curr: u64 = 1;
for _ in 1..n {
let result = prev.checked_add(curr);
match result {
Some(next) => {
prev = curr;
curr = next;
}
None => {
curr = 0;
break;
}
}
}
match curr == 0 {
false => Ok(curr),
true => Err("Calculation overflow")
}
}</code></pre>
<div><i>
Side Note: In thinking about this later, I could have done the pattern
matching more elegantly. I started with an if/else block (which is why I'm
matching on a boolean value). But we could also write the pattern matching to
use the "curr" value directly:
</i></div>
<pre><code> match curr {
0 => Err("Calculation overflow"),
_ => Ok(curr),
}</code></pre>
<div><i>This is more direct (but not really less confusing since 0 is a magic number here). Now the match is on the "curr" value itself. If the value is "0", then we return the Err. For the second arm, the underscore represents a catch-all. So if the value is anything other than "0", we return "Ok". Notice that I did have to reverse the order of the arms. The first match wins with pattern matching, so the default case needs to be at the end.</i></div><div><br /></div><div><i>Both of these matches produce the same results. We won't worry about them too much because we'll be replacing this entirely in just a bit.</i></div><div><br /></div>
<div>
But since we changed the return type, our calling code needs to be updated.
</div>
<div><br /></div>
<div><b>Using ".expect"</b></div>
<div>
One way that we can deal with the Result enum is to use the "expect()"
function.
</div>
<div><br /></div>
<div>Here is the updated code from the "main" function:</div>
<pre><code> println!("Fibonacci {} = {}", nth, fib(nth).expect("Fibonacci calculation failed"));</code></pre>
<div>
After the call to "fib(nth)", we add an ".expect()" call and pass in a
message.
</div>
<div><br /></div>
<div>
"expect()" works on a Result enum. If the Result is "Ok", then it pulls out
the value and returns it. So if there is no overflow, then the expected
Fibonacci number is used for the placeholder.
</div>
<div><br /></div>
<div>
But if Result is "Err", then "expect" will panic. That's not exactly what we
want here, but this gets us one step closer.
</div>
<div><br /></div>
<div>With the "expect" in place, here is our output:</div>
<pre><code> Fibonacci 1 = 1
Fibonacci 2 = 1
Fibonacci 3 = 2
Fibonacci 4 = 3
Fibonacci 5 = 5
Fibonacci 6 = 8
Fibonacci 7 = 13
Fibonacci 8 = 21
Fibonacci 9 = 34
Fibonacci 10 = 55
Fibonacci 90 = 2880067194370816120
Fibonacci 91 = 4660046610375530309
Fibonacci 92 = 7540113804746346429
Fibonacci 93 = 12200160415121876738
thread 'main' panicked at 'Fibonacci calculation failed: "Calculation overflow"', src \main.rs:5:53
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\fib.exe` (exit code: 101)</code></pre>
<div>
Because we get a panic when trying to fetch #94, the application halts and
does not process the rest of the values (95 and 96).
</div>
<div><br /></div>
<div>
If we look at the output, we see both of the messages that we added.
"Fibonacci calculation failed" is what we put into the "expect", and
"Calculation overflow" is what we put into the "Err" Result.
</div>
<div><br /></div>
<div>
But I'd like to get rid of the panic. And we can do that with more pattern
matching.
</div>
<div><br /></div>
<h3 style="text-align: left;">Matching on Result</h3>
<div>
<b>Branch: <a href="https://github.com/jeremybytes/fibonacci-rust/blob/06-matchresult/src/main.rs">06-matchresult</a></b>
</div>
<div>
Just like we used pattern matching with Option in the "fib" function, we can
use pattern matching with Result in the "main" function.
</div>
<div><br /></div>
<div><b>Pattern Matching</b></div>
<div>Here's the updated "for" loop from the "main" function:</div>
<pre><code> for nth in nths {
match fib(nth) {
Ok(result) => println!("Fibonacci {} = {}", nth, result),
Err(e) => println!("Error at Fibonacci {}: {}", nth, e),
}
}</code></pre>
<div>Inside the "for" loop, we match on the result of "fib(nth)".</div>
<div><br /></div>
<div>
If the Result is "Ok", then we use "println!" with the same string that we had
before.
</div>
<div><br /></div>
<div>If the Result is "Err", then we output an error message.</div>
<div><br /></div>
<div><b>Adding an Overflow Flag</b></div>
<div>
The last thing I want to do is get rid of the "curr = 0" that denotes an
overflow. Even though this works, it's a bit unclear. (And it can cause
problems since some implementations of Fibonacci consider "0" to be a valid
value.)
</div>
<div><br /></div>
<div>
For this, we'll add a new variable called "overflow" to the "fib" function.
Here's the completed function with "overflow" in place:
</div>
<pre><code> fn fib(n: u8) -> Result<u64, &'static str> {
let mut prev: u64 = 0;
let mut curr: u64 = 1;
let mut overflow = false;
for _ in 1..n {
let result = prev.checked_add(curr);
match result {
Some(next) => {
prev = curr;
curr = next;
}
None => {
overflow = true;
break;
}
}
}
match overflow {
false => Ok(curr),
true => Err("Calculation overflow")
}
}</code></pre>
<div>
A new mutable "overflow" variable is created and set to "false". Then if there
is an overflow, it is set to "true". Finally, "overflow" is used in the final
pattern matching to determine whether to return "Ok" or "Err".
</div>
<div><br /></div>
<div><b>Final Output</b></div>
<div>With these changes in place, here is our final output:</div>
<pre><code> Fibonacci 1 = 1
Fibonacci 2 = 1
Fibonacci 3 = 2
Fibonacci 4 = 3
Fibonacci 5 = 5
Fibonacci 6 = 8
Fibonacci 7 = 13
Fibonacci 8 = 21
Fibonacci 9 = 34
Fibonacci 10 = 55
Fibonacci 90 = 2880067194370816120
Fibonacci 91 = 4660046610375530309
Fibonacci 92 = 7540113804746346429
Fibonacci 93 = 12200160415121876738
Error at Fibonacci 94: Calculation overflow
Error at Fibonacci 95: Calculation overflow
Error at Fibonacci 96: Calculation overflow</code></pre>
<div>
This version no longer panics if there is an overflow. If we do have an
overflow, it gives us an error message. And all of the values will get
calculated, even if an overflow occurs in the middle.
</div>
<div><br /></div>
<h3 style="text-align: left;">Wrap Up</h3>
<div>
Calculating Fibonacci numbers is not a very complex task. But in this
walkthrough, we got to use several features of Rust and understand a bit more
about how the language works.
</div>
<ul>
<li><code>for</code> loops</li>
<li>Statements vs. expressions</li>
<li>Function returns (expressions)</li>
<li><code>checked_add</code> to prevent overflow</li>
<li>Option enum (returned from <code>checked_add</code>)</li>
<li>Pattern matching on Option</li>
<li>Result enum (to return error rather than panic)</li>
<li><code>.expect</code> with Result</li>
<li>Pattern matching on Result</li>
</ul>
<div>
Quite honestly, I didn't expect to get this much out of this exercise. I've
calculate Fibonacci sequences lots of times before. I was surprised about what
I learned.
</div>
<div><br /></div>
<div>
It's okay to do "simple" exercises. And it's okay to be surprised when they
don't go quite as you expected.
</div>
<div><br /></div>
<div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com1tag:blogger.com,1999:blog-5359546512544809971.post-12416802587550150042021-09-26T08:48:00.000-07:002021-09-26T08:48:17.479-07:00Initial Impressions of Rust<div>
I experimented a little with Rust this past week. I haven't gone very deep at
this point, but there are a few things I found interesting. To point some of
these out, I'm using a number guessing game (details on the sample and where I got it are
at the end of the article). The code can be viewed here: <a href="https://github.com/jeremybytes/guessing-game-rust">https://github.com/jeremybytes/guessing-game-rust</a>.
</div>
<div><br /></div>
<div>
<i>Disclaimer: these are pretty raw impressions. My opinions are subject to
change as I dig in further.</i>
</div>
<div><br /></div>
<div>
The code is for a "guess the number" game. Here is a completed game (including
both input and output):
</div>
<pre><code>
Guess the number!
Please input your guess.
23
You guessed: 23
Too small!
Please input your guess.
78
You guessed: 78
Too big!
Please input your guess.
45
You guessed: 45
Too small!
Please input your guess.
66
You guessed: 66
Too small!
Please input your guess.
71
You guessed: 71
You win!</code></pre>
<div>
So let's get on to some of the language and environment features that I find
interesting.
</div>
<div><br /></div>
<h3 style="text-align: left;">Automatic Git</h3>
<div>
Cargo is Rust's build system and package manager. The following command will create a
new project:
</div>
<div><br /></div>
<div><code>cargo new guessing-game</code></div>
<div><br /></div>
<div>
Part of the project creation is a new Git repository (and a .gitignore file).
So there are no excuses about not having source control.
</div>
<div><br /></div>
<h3 style="text-align: left;">Pattern Matching</h3>
<div>
Here's a pattern matching sample (from the
<a href="https://github.com/jeremybytes/guessing-game-rust/blob/main/src/main.rs">main.rs file</a>
in the repository mentioned above):
</div>
<pre><code>
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
},
}
</code></pre>
<div><br /></div>
<div>
In this block "guess.cmp(&secret_number)" compares the number the user
guessed to the actual number. "cmp" returns an Ordering enumeration. So
"Ordering::Less" denotes the "less than" value of the enumeration.
</div>
<div><br /></div>
<div>
Each "arm" of the match expression has the desired functionality: either
printing "Too small!", "Too big!", or "You win!".
</div>
<div><br /></div>
<div>
<i>
As a couple of side notes: the "println!" (with an exclamation point) is a
macro that prints to the standard output. I haven't looked into macros yet,
so that will be interesting to see how this expands. Also the "break" in the
last arm breaks out of the game loop. We won't look at looping in this
article.
</i>
</div>
<div><br /></div>
<h3 style="text-align: left;">Error Handling</h3>
<div>
I like looking at different ways of approaching error handling. Earlier this
year, I wrote about the approach that Go takes: <a href="https://jeremybytes.blogspot.com/2021/01/go-golang-error-handling-different.html">Go (golang) Error Handling - A Different Philosophy</a>. Go differs quite a bit from C#. While C# uses exceptions and try/catch
blocks, Go uses strings - it's up to the programmer to specifically check for
those errors.
</div>
<div><br /></div>
<div>
Rust takes an approach that is somewhere in between by using a Result
enumeration. It is common to return a Result which will provide an "Ok" with
the value or an "Err".
</div>
<div><br /></div>
<div>
Let's look at 2 approaches. For this, we'll look at the part of the program
that converts the input value (a string) into a number.
</div>
<div><br /></div>
<div><b>Using "expect"</b></div>
<div>
Let's look at the following code (also from the
<a href="https://github.com/jeremybytes/guessing-game-rust/blob/main/src/main.rs">main.rs file</a>):
</div>
<pre><code>
let guess: u32 = guess.trim().parse()
.expect("invalid string (not a number)");
</code></pre>
<div>
This code parses the "guess" string and assigns it to the "guess" number (an
unsigned 32-bit integer). We'll talk about why there are two things called
"guess" in a bit.
</div>
<div><br /></div>
<div>
The incoming "guess" string is trimmed and then parsed to a number. This is
done by stringing functions together. But after the parse, there is another
function: expect.
</div>
<div><br /></div>
<div>
The "parse" function returns a Result enumeration. If we try to assign this
directly to the "guess" integer, we will get a type mismatch. The "expect"
function does 2 things for us. (1) If "parse" is successful (meaning Result is
"Ok"), then it returns the value that we can assign to the variable. (2) If
"parse" fails (meaning Result is "Err"), then our message is added to the
resulting error.
</div>
<div><br /></div>
<div>Here's a sample output:</div>
<pre><code>
Guess the number!
Please input your guess.
bad number
thread 'main' panicked at 'invalid string (not a number): ParseIntError { kind: InvalidDigit }', src\main.rs:19:14
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\guessing_game.exe` (exit code: 101)
</code></pre>
<div>
I typed "bad number", and the parsing failed. This tells us that "'main'
panicked", which means that we have an error that caused the application to
exit. And the message has our string along with the actual parsing error.
</div>
<div><br /></div>
<div><b>Pattern Matching</b></div>
<div>
Another way of dealing with errors is to look at the Result directly. And for
this, we can use pattern matching.
</div>
<div><br /></div>
<div>Here is the same code as above, but we're handing the error instead:</div>
<pre><code>
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(msg) => {
println!("{}", msg);
continue;
},
};
</code></pre>
<div>
Notice that we have a "match" here. This sets up a match expression to use the
Result that comes back from the "parse" function. This works similarly to the
match expression that we saw above.
</div>
<div><br /></div>
<div>
If Result is "Ok", then it returns the value from the function ("num") which
gets assigned to the "guess" integer.
</div>
<div><br /></div>
<div>
If Result is "Err", then it prints out the error message and then continues.
Here "continue" tells the containing loop to go to its next iteration.
</div>
<div><br /></div>
<div>
<i>Side note: The "println!" macro uses placeholders. The curly braces within
the string are replaced with the value of "msg" when this is printed.</i>
</div>
<div><br /></div>
<div>Here is some sample output:</div>
<pre><code>
Guess the number!
Please input your guess.
bad number
invalid digit found in string
Please input your guess.
23
You guessed: 23
Too small!
</code></pre>
<div>
This time, when I type "bad number" it prints out the error message and then goes to
the next iteration of the loop (which asks for another guess).
</div>
<div><br /></div>
<div>
Overall, this is an interesting approach to error handling. It is more
structured than Go and its error strings but also a lot lighter than C# and
its exceptions. I'm looking forward to learning more about this and seeing
what works well and where it might be lacking.
</div>
<div><br /></div>
<h3 style="text-align: left;">Immutability</h3>
<div>
Another feature of Rust is that variables are immutable by default. Variables
must be made explicitly mutable.
</div>
<div><br /></div>
<div>
Here is the variable that is used to get the input from the console (from the
same
<a href="https://github.com/jeremybytes/guessing-game-rust/blob/main/src/main.rs">main.rs file</a>):
</div>
<pre><code>
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("failed to read line");
</code></pre>
<div>
The first line creates a mutable string called "guess". The next line reads
from the standard input (the console in this case) and assigns it to the
"guess" variable.
</div>
<div><br /></div>
<div>
<i>A couple more notes: You'll notice the "&" in the "read_line" argument.
Rust does have pointers. Also the double colon "::" denotes a static. So
"new" is a static function on the "String" type.</i>
</div>
<div><br /></div>
<div>
Immutable by default is interesting since it forces us into a different
mindset where we assume that variable cannot be changed. If we want to be able
to change them, we need to be explicit about it.
</div>
<div><br /></div>
<h3 style="text-align: left;">Variable Shadowing</h3>
<div>
The last feature that we'll look at today is how we can "shadow" a variable.
In the code that we've seen already, there are two "guess" variables:
</div>
<pre><code>
let mut guess = String::new();
</code></pre>
<div>
This is a string, and it is used to hold the value typed in on the console.
</div>
<pre><code>
let guess: u32 = guess.trim().parse()
</code></pre>
<div>
This is a 32-bit unsigned integer. It is used to compare against the actual
number that we are trying to guess.
</div>
<div><br /></div>
<div>
These can have the same name because the second "guess" (the number) "shadows"
the first "guess" (the string). This means that after the second "guess" is
created, all references to "guess" will refer to the number (not the string).</div>
<div><br /></div>
<div>
At first, this seems like it could be confusing. But the explanation that goes
along with this code helps it make sense (from
<a href="https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html">The Rust Programming Language, Chapter 2</a>):
</div>
<blockquote>
<div>
We create a variable named guess. But wait, doesn’t the program already have
a variable named guess? It does, but Rust allows us to shadow the previous
value of guess with a new one. This feature is often used in situations in
which you want to convert a value from one type to another type. Shadowing
lets us reuse the guess variable name rather than forcing us to create two
unique variables, such as guess_str and guess for example.
</div>
</blockquote>
<div>
I often use intermediate variables in my code -- often to help make debugging
easier. Instead of having to come up with unique names for variable that
represent the same thing but with different types, I can use the same name.
</div>
<div><br /></div>
<div>
I'm still a bit on the fence about this. I'm sure that it can be misused (like
most language features), but it seems to make a lot of sense in this
particular example.
</div>
<div><br /></div>
<h3 style="text-align: left;">Rust Installation</h3>
<div>One other thing I wanted to mention was the installation process on Windows. Rust needs a C++ compiler, so it recommends that you install the "Visual Studio C++ Build Tools". This process was not as straight forward as I would have liked. Microsoft does have a stand-alone installer if you do not have Visual Studio, but it starts up the Visual Studio Installer. I ended up just going to my regular Visual Studio Installer and checking the "C++" workload. I'm sure that this installed <b>way</b> more than I needed (5 GBs worth), but I got it working on both of my day-to-day machines.</div><div><br /></div><div>Other than the C++ build tools, the rest of the installation was pretty uneventful.</div><div><br /></div><div>I'm assuming that installation is a bit easier on Unix-y OSes (like Linux and macOS) since a C++ compiler (such as gcc) is usually already installed.</div><div><br /></div><div>There is also a way to run Rust in the browser: <a href="https://play.rust-lang.org/">https://play.rust-lang.org/</a>. I haven't played much with this, so I'm not sure what the capabilities and limitations are.</div><div><br /></div><div>For local editing, I've been using Visual Studio Code with the Rust extension.</div><div><br /></div><h3 style="text-align: left;">Documentation and Resources</h3><div>The sample code is taken from The Rust Programming Language by Steve Klabnik and Carol Nichols. it is available <a href="https://doc.rust-lang.org/book/title-page.html">online</a> (for free) or in <a href="https://nostarch.com/Rust2018">printed form</a>.</div><div><br /></div><div>I have only gone as far as Chapter 2 at this point. I really liked Chapter 2 because it walks through building this guessing game project. With each piece of code, various features were showcased, and I found it to be a really good way to get a quick feel for how the language works and some of the basic paradigms.</div><div><br /></div><div><br /></div><h3 style="text-align: left;">Wrap Up</h3><div>I'm not sure how far I'll go with Rust. There are definitely some interesting concepts (that's why I wrote this article). Recently, I converted one of my applications to Go to get a better feel for the language and stretch my understanding a bit (<a href="https://github.com/jeremybytes/digit-display-golang">https://github.com/jeremybytes/digit-display-golang</a>). I may end up doing the same thing with Rust.</div><div><br /></div><div>Exploring other languages can help us expand our ways of thinking and how we approach different programming tasks. And this is useful whether or not we end up using the language in our day-to-day coding. Keep expanding, and keep learning.</div><div><br /></div><div>Happy Coding!</div>
Jeremyhttp://www.blogger.com/profile/06749690234470413216noreply@blogger.com1