Errata

C# Cookbook

Errata for C# Cookbook

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released. If the error was corrected in a later version or reprint the date of the correction will be displayed in the column titled "Date Corrected".

The following errata were submitted by our customers and approved as valid errors by the author or editor.

Color key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted By Date submitted Date corrected
Printed, PDF, ePub, Mobi, O'Reilly learning platform, Other Digital Version
Page Page 175
Top of Solution code listing and bottom of the page where I recommend NuGet packages.

A reader reached out on the GitHub csharp-nine-cookbook repository Issues to explain a problem with the sample.

The current code uses the path to the Python library as "sys.path.append(path)". The problem is that this could cause a version mismatch, resulting in the code throwing an exception, "Microsoft.Scripting.SyntaxErrorException", or module not found exception, "IronPython.Runtime.Exceptions.ImportException".

The recommended solution is to remove the "sys.path.append(path)" and add the "IronPython.StdLib" NuGet package to the project.

Joe Mayo
Joe Mayo
 
Nov 13, 2023 
Page 3
The Dispose(bool) method

I am looking forward to reading this book, but I am somewhat stuck on the first recipe: "1.1 Managing Object End-of-Lifetime". Memory management is an important topic, so I want to make sure I get it right. I have 2 main questions:

1. On page 3, report?.Close() is called outside/after the if-statement which checks for disposing. Why is that? My thinking is that since report is a StreamWriter and StreamWriter is a managed object -- albeit one which internally manages an unmanaged resource -- the call to report.Close() should go inside the if(disposing) block. If the placement in the book is correct, what should run when IDisposable::Dispose() is called vs when the finalizer is called?

2. The call to report?.Close() uses the null-conditional member access operator. Why is that? My initial thinking was that we are checking in case the object referenced by report was collected prior to collecting the owning DeploymentProcess object, but that would depend on the garbage collector setting the DeploymentProcess::report instance variable to null upon collection. Does it do that? If not, it seems like the call to report?.Close() would cause undefined behavior since the report variable would still contain the reference to some memory which may have already been collected and possibly overwritten.

Thanks!

Note from the Author or Editor:
Hi Adam,

On question #1, `StreamWriter` is a type in the .NET Framework and seems like it should be treated as a managed object. I thought the same way and received feedback that made me see how it could be viewed another way. It revolves around the fact that the stream is for a file and the underlying implementation for Windows OS files is naturally via a Windows File Handle (https://docs.microsoft.com/en-us/windows/win32/fileio/file-handles), which is an unmanaged resource. The implications of this is that failing to release that resource could either prevent this or other apps from accessing that file or the OS runs out of file handles and an app or the whole system starts to crash.

Summarizing, the reason for the `if (disposing)` is to make sure that managed resources only dispose one time, preventing ObjectDisposedException. The way this example is written, the ObjectDisposedException scenario won't happen because of the call to `GC.SuppressFinalize`. If the calling code never called `Dispose`, the Finalizer would run and the call to `report?.Close()` would definitely happen. Since the object is a private field of this class, no-one else can Dispose it and we don't have to worry about the ObjectDisposedException. In another scenario, even if this object were passed in via constructor or some other possibility where it was set to null and the GC collected it, we have to look at the tradeoff of which is more evil - fail to release the file handle or run the weird risk of an intermittent ObjectDisposedException.

Generally, if I can convince enough developers to just do this when it's necessary, their code would be much better. If that's done, you'll be in a good place for this deep analysis of picking the least worse trade-off.

On question #2, I see your point and the null-conditional member access on `report?.Close()` isn't necessary in this example. Perhaps it was part of a habit that feeds my personal need for defensive programming (paranoia). That said, I've seen times in the past where this was necessary:

* If `report` was passed in a constructor, we don't know what other code did with it.
* If `report` was set to `null` inside of the class: maybe an elaborate disposal process that set it to `null`.
* Future maintenance that set it to `null` or an inadvertent bug.

Essentially, this prevents a `NullReferenceException` during the disposal process. Especially if the disposal path is through the Finalizer where this would be intermittent and hard for other developers to debug. It doesn't cause any problems because if `report` is null, nothing bad happens, others it calls `Close()` like normal

Tangentially, you're going to see me do things like this throughout the book because I have an ulterior motive - there's something about this that you'll see in the future. In this case, Chapter 3 has a recipe on a C# 8 feature called Nullable Reference types. In a nutshell (O'Reilly pun), Nullable Reference types improve the quality of your code by avoiding `NullReferenceExceptions`. Normally I give you better clues with mentioning a future recipe or adding a See Also reference. Now, I'm wondering if this would have been a good opportunity to do that in this case.

Thank you for your feedback and the opportunity to share my thoughts.

Regards,

Joe

Adam Dumas  Jan 24, 2022 
Page 158
2nd paragraph, second-to-last sentence

The sentence reads, "With a new instance of the GeneratorBase-derived type, CreateGenerate returns to ReportGenerate, which calls Generate on the new instance." In the context of the example this sentence refers to there is no "CreateGenerate" method but instead there is a "CreateGenerator" method, indicating this is a typo.

Note from the Author or Editor:
Thank you for reporting these typeos - I found another in the same sentence. Here's the re-written sentence:

"With a new instance of the GeneratorBase-derived type, CreateGenerator returns to Report.Generate, which calls Generate on the new instance."

Kenneth Hampton  Jul 14, 2022 
Page 209
4th paragraph

The paragraph mentions several times "OperationCancelledException". The correct name of the class is "OperationCanceledException".

Note from the Author or Editor:
Thank you for the errata report. All occurrences of "OperationCancelledException" should be changed to "OperationCanceledException" on page 209 because that is the proper name of the class in the code (with one l).

Kenneth Hampton  Aug 02, 2022 
Printed, PDF, ePub, Mobi, , Other Digital Version
Page 216
GenerateMD5Hash and GenerateSha256Hash

Methods don't fill in the `saltedPassword` `byte[]`. To fix, add two lines of `Array.Copy` after instantiating `saltedPassword`, like this:

```C#
byte[] saltedPassword =
new byte[salt.Length + passwordBytes.Length];

Array.Copy(passwordBytes, saltedPassword, passwordBytes.Length);
Array.Copy(salt, 0, saltedPassword, passwordBytes.Length, salt.Length);
```

I've also updated the source code on GitHub:

https://github.com/JoeMayo/csharp-nine-cookbook

Joe Mayo
Joe Mayo
 
Jun 15, 2022