C# in Depth

Cover of C# in Depth
Order now (3rd edition)

(You are currently looking at the first edition version of this page. This page is also available for the second and third editions.)

All Errata

P xi, TOC: Chapter 4 summary is section 4.5

In the table of contents (page xi) the summary on page 136 is included as part of section 4.4. It should be listed as section 4.5.

P 13, final paragraph: Wording around null comparisons

When introducing nullable values, I say this in the book:

a product with an unknown price will be considered to be less expensive than $10, which is probably what we'd want.

It's not clear that this is because we use the greater-than operator to do the comparison with $10, and that greater than and less than comparisons with null values are always false. So, if instead of price > 10m our comparison had been !(price <= 10m) (a comparison which looks like it will do the same thing) we'd get the wrong answer.

This is fully explained on P125 when discussing nullable types in more depth.

P 18: Timeframe or time frame?

At the end of the second paragraph of page 18, I've used "timeframe" as one word. Apparently it's more commonly written as "time frame" so it should probably be written that way in the book.

P 45, bottom line: Font fail

On page 45, on the bottom line, strings and objects should both be in the code font:

both strings and objects refer to the same array.

Currently the 's' at the end of each word is in the normal font. Oops.

P50, Figure 2.3: Incorrect label for bottom right box of figure 2.3

On the right hand side of figure 2.3 ("When Point is a value type") the bottom box should be labeled p2 instead of p1.

I have produced a corrected PDF of the figure.

Thanks to David Gadd for spotting this and pointing it out.

P57: Covariance/contravariance - just what isn't improved in C# 2?

In the first edition, lines 3-5 of page 57 read:

C# 2 doesn't tackle the general issue of covariant return types and covariant parameters, but it does cover it for creating delegate instances in certain situations, as we saw in section 2.4.1.

It's not terribly clear what the "general issue" is. The following would be clearer:

C# 2 doesn't tackle the issue of return type covariance and parameter contravariance for overriding members or implementing interfaces. However, it does improve the situation for delegate instance creation in certain situations, as we saw in section 2.4.1.

P 67, first paragraph: Only one empty string, not two - and Snippy oddities

The text in the first paragraph on P67 currently includes:

It's crude - we end up with two empty strings (one at each end of the text)

In fact, we only get a single empty string - it's due to the period at the end of "green eggs and ham." The regular expression sees a non-word character, so splits "ham." into "ham" and "".

As an aside, this program currently works pretty weirdly in Snippy because it uses a #line directive before every single line to make the error messages work properly. That's fine normally, but not when there's a verbatim string literal involved! The text variable ends up with this text:

Do you like green eggs and ham?
#line 22
                        I do not like them, Sam-I-am.
#line 23
                        I do not like green eggs and ham

I'll try to fix it, but it's fun anyway...

P 68, Figure 3.1: Solidifying lines

In figure 3.1, there are various lines which could be considered to be "dashed lines". Unfortunately I refer to just one set of them on page 69, so it would be better if the others weren't dashed.

The long vertical line splitting the left side from the right should be solid but very thin (or grey). The lines labeled "Instantiation" can be turned into solid lines. This leaves just the lines labeled "Specification of type arguments" as dashed lines.

P 68, Note "Jargon alert": Moving the warning symbol

The warning symbol which is currently at the end of the note on P68 would be more appropriate in the left margin, which is where all the similar icons are placed throughout the book.

P 79: Invalid type constraint listed as valid

On page 79, I include the following constraint as a legal one:

// Not valid after all!
class Sample<T> where T : class, Stream, new()

This is in fact not a valid constraint, for a reason I wasn't aware of:

If you have a derivation type constraint where the type is a class (such as Stream) you can't also specify either class or struct (the reference and value type constraints respectively).

For any class other than Object, ValueType and Enum, any derived classes will be reference types anyway, so the constraint doesn't do you any good. Note that you can specify an interface for the derivation type constraint and still specify class/struct. For example:

// This is valid though
class Sample<T> where T : class, IDisposable, new()

For the next printing of the book, we will simply change the incorrect line to the valid example directly above - there isn't enough room to explain why the top version is invalid without reflowing the page - but if there's ever a comprehensive update to the book, I'll add a full explanation there.

P 82: Comparing values of [type] T

In the section "Direct Comparisons" in the second paragraph, third line, the sentence:

You can't compare two values of T with each other.

This would be clearer as:

You can't compare two values of type T with each other.

(Change is marked in bold.)

P 98: Missing words in final paragraph of P98

On page 98, the sentence beginning:

Making individual calls to a collection doesn't make the collection thread-safe,

should instead begin:

Making individual calls to a collection thread-safe doesn't make the collection itself thread-safe,

(Changes highlighted in bold.)

P105: Location of "Invalid" marker on first code snippet

In the first code snippet, it's only the final line which is actually invalid. It would be helpful if the "Invalid" arrow pointed directly at this line.

P109: Early Java-like generics

The last sentence on page 109 ends with:

several years after other projects had compilers for their Java-like languages.

This would be clearer as:

several years after other projects had created Java-like languages including generics.

P110, first bullet: Strict versioning in .NET

The first bullet on page 110 ends with these sentences:

Versioning in .NET is much stricter in general - you have to compile using the oldest environment you want to run on. That's safer, but less flexible.

This is misleading in the light of Visual Studio 2008, which can target .NET 2.0. The following would be clearer:

Versioning in .NET is much stricter in general - for each assembly you reference, you can specify whether or not the version number has to match exactly. In addition, code built to run on the 2.0 CLR will not run on .NET 1.1.

P124, bottom paragraph: Accidental reversal of int and long in example

In the commentary on lifted conversions with nullable types, I use int and long as the examples for the S and T types in the specification. Unfortunately, in one place I got them the wrong way round. This sentence:

To carry our example forward, this means that you can convert implicitly from int? to long? and from int to long? as well as explicitly from long? to int.

should read

To carry our example forward, this means that you can convert implicitly from int? to long? and from int to long? as well as explicitly from int? to long.

(note the change in the last few words). In fact, it's still correct - there is an explicit conversion from long? to int, but only because of the explicit conversion from long to int.

P131, end of intro to 4.4: Incorrect cross-reference

The introductory paragraph to section 4.4 (in the middle of the page) ends with a reference to section 3.2.6. Unfortunately, there is no such section - although if you keep counting from 3.2.3 to 3.3.1 you'll see some of the history of the book...

The correct reference should be to 3.3.3 and in particular the note in the middle of page 81.

P141, start of 5.3: Incorrect cross reference

At the start of section 5.3, I refer back to section 2.3.2 for discussion of covariance and contravariance. This should in fact be a reference to 2.2.2.

P155, first paragraph: Variable "i" should be "counter"

The second sentence on page 155 currently reads:

The first line of output comes from the invocation of the delegate instance within CreateDelegateInstance, so it makes sense that the value of i is available at that point.

Unfortunately, there is no variable i - there's counter instead! The sentence should read:

The first line of output comes from the invocation of the delegate instance within CreateDelegateInstance, so it makes sense that the value of counter is available at that point.

P157, first paragraph: Loop only displays up to 13, not 14

On page 157, in section 5.5.5, I claim that if we'd captured the index variable instead of counter, the output would have been the numbers 5 to 14. This isn't the case - it would actually be 5 to 13. Here's the code, ready to run in Snippy:

List<ThreadStart> list = new List<ThreadStart>();

for (int index=0; index < 5; index++)
   // counter variable is now obsolete
   int counter = index*10;
   list.Add (delegate
         // index variable is captured

foreach (ThreadStart t in list)


And here's the output...


Thanks to Dave Jordan for pointing this out.

P168, sequence diagram: Reformatting to distinguish columns more clearly

The textual sequence diagram on page 168 indicating how iterator blocks execute would be clearer if the second column were moved further to the right.

P180, first listing: Mismatch of variable names

The code at the top of page 180 declares the variable holdings but then uses it as stocks in the call to ProcessStocks. To be consistent with the code below, it's easiest just to change the variable to be declared as stocks:

Holdings stocks = DbService.EndGetStockHoldings(holdingsAsync);

P186, line 7: Incorrect bullet icon

The sentence beginning on line 7 of page 186 reads:

Only (1) specified any type constraints, and only (2) specified a base class.

The base class is specified at bullet point 3, so the sentence should read:

Only (1) specified any type constraints, and only (3) specified a base class.

P195: Emphasize introduction of WinForms class

Listing 7.6 has the WinForms::Button and WebForms::Button in bold, but not the introduction of class WinForms. It's easy to assume that the only changes between listing 7.5 and 7.6 are the changes to alias syntax, at which point it's not clear why you need it. The listing should look like this:

using System;
using WinForms = System.Windows.Forms;
using WebForms = System.Web.UI.WebControls;

class WinForms

class Test
   static void Main()
      Console.WriteLine (typeof (WinForms::Button));
      Console.WriteLine (typeof (WebForms::Button));

P212 and P213: Incorrect variable type claim

At the bottom of page 212, I use an example of:

var args = Environment.CommandLine;

and claim that args would be of type string[]. However, it would be of type string because that's the type of Environment.CommandLine. The same mistake is propagated on page 213, where I provide examples of using implicitly typed local variables for control structures, including this one:

foreach (var s in Environment.CommandLine)

I claim that s is of type string - but it would actually be of type char because string implements IEnumerable<char>.

The fix to both of these problems is simple - just replace the troublesome calls to Environment.CommandLine with Environment.GetCommandLineArgs() which really does return a string[]. The rest of the text is then accurate, and indeed it's the original meaning I intended.

P225, figure 8.3: Curious change in picture

In the published version of the book, the code in the foreach loop in figure 8.3 looks like this:

foreach (var person in family)
    totleAge += person.Age;

Note the typo - totle instead of total. The really odd thing is, I don't know how that mistake came about. Every copy of the image that I can find has the correct variable name. In particular, it would have had a red squiggle underneath it in its current form. Very strange. Anyway, here's what it should look like:

Fixed figure 8.3

P230: Incorrect use of "covariance"

On page 230 in the introduction to lambda expressions, I incorrectly talk about "parameter covariance". It should be "return type covariance" or "parameter contravariance" - both of which were added with C# 2.

P233, first text line: Extraneous comma

On page 233, in the first line of normal text, there's an extra comma in the quoted output. So where it says:

Listing 9.1 prints "5," just as we'd expect it to.

It should instead read:

Listing 9.1 prints "5" just as we'd expect it to.

The same issue comes up elsewhere in the text - we should probably use an automated "find" to check for all potential candidates.

P236: Briefer code in listing 9.4

The code in listing 9.4 can be abbreviated slightly. The lambda expression used to print the films list doesn't need braces. The code which appears like this:

Action<Film> print = film => { Console.WriteLine(film); };

can be changed to this:

Action<Film> print = film => Console.WriteLine(film);

This change has been made to the downloadable code. See also this note for even more significant abbreviations.

P238: Briefer code in listing 9.5

The code in listing 9.5 can be abbreviated slightly. The three lambda expressions don't need to have braces. The section which currently reads like this:

button.Click += (src, e) => { Log("Click", src, e); };
button.KeyPress += (src, e) => { Log("KeyPress", src, e); };
button.MouseClick += (src, e) => { Log("MouseClick", src, e); };

can be changed to this:

button.Click += (src, e) => Log("Click", src, e);
button.KeyPress += (src, e) => Log("KeyPress", src, e);
button.MouseClick += (src, e) => Log("MouseClick", src, e);

The changes have been made to the downloadable code.

P238: Swapped words

In the first paragraph of text on page 238, I state:

We don't log the details of the source event, beyond whatever its ToString override returns, because there's an overwhelming amount of information associated with controls.

The phrase "source event" is confusing. The sentence should read:

We don't log the details of the event source, beyond whatever its ToString override returns, because there's an overwhelming amount of information associated with controls.

i.e. "source event" becomes "event source" - basically the first parameter for the event handler.

P282, third paragraph: Nonsense sentence

The sentence in the third paragraph which currently reads:

There's currently has a single customer: Colin.

should read:

The company currently has a single customer: Colin.

P287, figure 11.4: Incorrect line placement

In figure 11.4 at the bottom of page 287, the middle line of the group labeled "Query expression contextual keywords" is meant to point at the word "in". I have prepared a replacement figure to show how it's meant to look.

P292: Missing "AllDefects" twice

On P292, near the top, I claim that:

from defect in SampleData.AllDefects
select defect

is equivalent to

SampleData.Select (defect => defect)

The latter is incorrect - it should actually be:

SampleData.AllDefects.Select (defect => defect)

The sentence following that should read:

There's a big difference between this and the simple expression SampleData.AllDefects, however.

P296, Figure 11.5: Typo in bottom box

The bottom box of figure 11.5 should read:

select new { Name = user.Name, Length = length }

I have prepared a replacement figure to show how it's meant to look.

P300, centre of page: Incorrect signature of "Join" method

The signature of the Join method in the middle of page 300 is incorrect - the innerKeySelector parameter should be of type Func<TInner,TKey>. In addition, it would be clearer to use TOuter instead of T. This affects the previous paragraph as well, giving this as the replacement:

Inner joins are translated by the compiler into calls to the Join method. The signature of the overload used for LINQ to Objects is as follows (when imagining it to be an instance method of IEnumerable<TOuter>):
IEnumerable<TResult> Join<TInner,TKey,TResult> (
   IEnumerable<TInner> inner,
   Func<TOuter,TKey> outerKeySelector,
   Func<TInner,TKey> innerKeySelector,
   Func<TOuter,TInner,TResult> resultSelector

This should be corrected for the second edition.

P303 and P306: More signature changes

The signatures of GroupJoin and SelectMany on pages 303 and 306 respectively are currently expressed as if they were methods of IEnumerable<T>. They might be clearer (and match MSDN) if the type parameters were named TOuter and TSource respectively. This would give the following signature on page 303:

IEnumerable<TResult> GroupJoin<TInner,TKey,TResult> (
   IEnumerable<TInner> inner,
   Func<TOuter,TKey> outerKeySelector,
   Func<TInner,TKey> innerKeySelector,
   Func<TOuter,IEnumerable<TInner>,TResult> resultSelector

and this signature on page 306, with a change to the last word of the preceding paragraph as well:

IEnumerable<TResult> SelectMany<TCollection,TResult> (
   Func<TSource,IEnumerable<TCollection>> collectionSelector,
   Func<TSource,TCollection,TResult> resultSelector

These changes are minor enough not to be worth making for simple reprints, but should be rolled into any more widespread reprints.

P317, second bullet: Renamed associations

On page 317, I mention in the second bullet that I renamed the parent and child properties used for the associations between Defect and DefectUser but I didn't say what I did with them. Anyone following along would have trouble with their code later on if they didn't happen to choose the same names as me. To fix this, add the following text to the end of the second bullet:

I named the relationships AssignedTo/AssignedDefects and CreatedBy/CreatedDefects.

P329, listing 12.7: Use the expression type in CreateQuery!

In the nongeneric implementation of IQueryProvider.CreateQuery, I'm ignoring the type of the expression and always returning a FakeQuery<object>. Instead, it should return a query based on expression.Type. The easiest way to do this is probably to call the generic method via reflection, or call the FakeQuery<T> constructor directly with Activator.CreateInstance.

P349, listing 12.22: Alignment in query

The alignment of the query in listing 12.22 is incorrect. It should be:

var query = from name in names.AsParallel(3)
            select ObtainLengthSlowly(name);