Important "versioning" note
This review tackles the first printing, from November 2007. Since then, the book has undergone more printings, with errata being fixed in each printing. I believe most or possibly all of the errors listed below are now fixed – although I don’t yet know whether there are more lurking. I have a recent (late 2009) printing which I intend to review when I have the time – which means it’s likely to be in 2010Q2 at the earliest, and more likely later. I don’t know how much editing has been done in terms of best/bad practice. I have left the review as I originally wrote it, as it is a fair (to my mind) representation of that first printing. (This is something to bear in mind with all reviews, mind you – check when they are written, and ideally which version they’re written about.) I’m reluctant to go as far as recommending the latest printing without actually reading it, but I’m fairly confident it’s a lot better than the first printing.
You should also bear in mind that many C# books – including Head First C#, the Essential C# book referenced in the review and my own book – are currently being revised for C# 4. If the HFC# 4 book comes out before I’ve had time to review the latest printing of the previous edition, I will probably go straight for that instead.
This is a tough review to write. We already know I’m biased due to being in some way in competition with Andrew Stellman and Jennifer Greene (the authors), but I’m also not a huge fan of the Head First series in general. It doesn’t coincide with how I like to learn. I feel patronised by the pictures and crosswords rather than drawn into them, etc. However, I’m very aware that it’s really popular – lots of people swear by it, and I know that my own tastes are far from that of the majority. There’s also the fact that Head First C# really isn’t aimed at me at all – it’s aimed at people who don’t know C# to start with.
Funnily enough, that’s why I really, really wanted to like the book. It’s not actually competing with C# in Depth except for readers who have no idea what either book is like. I can’t imagine many people being in the situation where both books would be appropriate for them. I do want to find a good book for someone to learn C# from though, from scratch. My own book is completely unsuitable for that purpose. Essential C# by Mark Michaelis is my current favourite, I think – although I confess to not having read it all. It also doesn’t cover C# 3 – although I’m not sure whether I’d actually want to cover C# 3 for a reader who doesn’t know 1 or 2.
Anyway, I had hoped that Head First C# (HFC# from now on) would become a good recommendation – and part of this is because Andrew and Jennifer have certainly been very friendly on the blog and email. I don’t like being nasty, and I know how I’d feel reading a review like this of my book. Unfortunately, it falls a long way short of being worthy of recommendation, and I can’t really find a way of hiding that.
I "read" the whole book today. Now, it’s over 700 pages and I’ve been at work, so clearly I haven’t read every word of every page. I have mostly skimmed the code examples – vital for learning, I’ll gladly admit, but not hugely necessary for getting the gist of the book. I skimmed over the graphics section particularly quickly, being more interested in the language and core library elements than UI topics. With that in mind, let’s look at what I did and didn’t like:
This is going to be a broadly negative review, but it’s not like the book is without merit. Here’s what I liked:
- Many of the examples were very nicely chosen. I liked the party organizer (chapter 5) and the room topology (chapter 7) ones in particular.
- You really do get to create some nifty WinForms applications. I wouldn’t like to claim you really understand everything about them by the end of it, but I can see how they’re engaging.
- Annotations in listings are a good thing in general, and I do like the handwritten feel of them.
- Q&A is a good idea for a book like this. Predicting natural questions is an important part of
- After a slightly scary chapter 1 (building an addressbook using a database without actually knowing any C#) it does start from the real beginning, to some extent.
- The pictures in this book were actually not annoying, to my surprise. Some were even endearing. Even to me.
- Apparently other people really like this book. It’s got loads of 5-star reviews on Amazon, and apparently it’s been selling fantastically well. From the posts in the book’s forum, people are really getting engaged, which can only be a good thing.
I know the Head First series is all about its wacky style, etc – but I still find it can take a while to work out how you’re meant to navigate through the page. Often bits of the page do require a certain order, but that order isn’t always obvious.
My main problem with the formatting wasn’t nearly as general as that though, and I have to admit it’s a bit obsessive-compulsive. It’s the quotes in the code. They’re not straight. I’ve never seen an IDE which tries to work out "curly quotes" in code, and I hope I never do. When you’re used to seeing code in a real IDE, seeing curly quotes just looks wrong. It’s jarring and distracts from the business of learning.
Oh, and K&R bracing sucks. I know it makes the book shorter, but it makes it harder to read too. Just a personal opinion, of course :) (Having said which, the bracing style is inconsistent through the book anyway.)
I’m unashamedly a "bottom-up" person when it comes to learning. I like to get hold of several small building blocks, understand them to a significant degree, and then see what happens when I put them together. This book prefers the "show it all and explain some bits as we go along" approach, reasonably frequently mentioning how awful it is that most technical books try to start with console applications which don’t need lots of libraries before you even know what a library is.
I suspect this top-down way does indeed get people going much more quickly than my preferred style. (Remember, this is my preferred reading style, not just writing style. It’s no coincidence that I happen to write like I read though.) However, I believe there’s a great danger of people ending up as cargo-cult programmers. (I know I refer to that blog entry a lot. It happens to rock, and be relevant to much of why I write how/what I write.)
It’s true that top-down learning gets you doing flashy stuff more quickly than bottom-up. Whether that’s more fun or not depends on what appeals to you – I get a great sense of joy from thinking about a difficult topic and gradually understanding it, even if I have nothing to show for it beyond console apps which do little but sort numbers etc.
It feels like there ought to be a middle way, but I’ve no idea what it is yet. The "show something cool in chapter 1 and then go back over the details" approach favoured by the vast majority of technical books (including mine, to some extent) isn’t what I’m thinking of. Definitely something to think about there.
I hadn’t expected this book to be in great depth, despite the quote on the back cover: "If you want to learn C# in depth and have fun doing it, this is THE book for you." I’d expected a bit more than this, however.
When I said earlier on that HFC# wasn’t competing with C# in Depth, I mentioned audiences. There’s something to be said about material as well though. Let’s look at the bulk of my book – chapters 3-11, which deal with the new language features from C# 2 and 3. Here’s how much is covered in HFC#:
- Generics: generic collections get mentioned, but there’s no real explanation of how you’d write your own generic types. Generic methods aren’t mentioned. Type constraints, default(…), variance (or lack thereof), type inference – all absent.
- Nullable types: not mentioned as far as I can see, including an absence of the null-coalescing operator.
- Delegates: there’s a chapter on delegates which deals entirely with events and callbacks. No mention of their use in LINQ. No use of method group conversions, anonymous methods or support for variance. (In fact, there are some false claims around that, saying that the signature of an event handler has to match the delegate type exactly.)
- Iterator blocks: IEnumerable<T> is mentioned, but IEnumerator<T> is never shown as far as I remember, and iterator blocks certainly aren’t shown. It mentions that you could implement IEnumerable<T> yourself, but doesn’t give any hints as to how, or what would be required.
- Partial types: covered, albeit only mentioning classes. No sign of partial methods.
- Static classes: covered to some extent, but without explaining that they prevent you from attempting to use the class inappropriately, or other details like the absence of constructors (not even a default one).
- Separate getter/setter access: covered
- Namespace aliases: not covered
- Pragma directives: not covered
- Fixed size buffers: not covered
- InternalsVisibleTo: not covered (not really a language feature though)
- Automatically implemented properties: covered, but without mentioning (AFAICR) that a hidden backing field is generated for you
- Implicit typing: one call-out and a somewhat inaccurate Q&A
- Object/collection initializers: covered, but without noting that you can remove the brackets from parameterless constructor calls
- Implicitly typed arrays: not covered
- Anonymous types: mentioned in a single annotation, but inaccurately. Used in a few query expressions.
- Lambda expressions: not mentioned (in a book which "covers" C# 3.0. Wow.)
- Expression trees: not mentioned
- Changes to type inference/overloading: hard to explain a change to something which isn’t covered to start with
- Extension methods: covered
- Query expressions: covered to a very limited extent. No explanation of query translation. No explanation of query continuations (although grouping is always shown with continuations). No sign of multiple "from" statements or "join into".
See why I don’t think our books are really competing? Now, a lot of this really is absolutely fine. The details of generics don’t really belong in an introductory text – although some more information would have been welcome. Pragmas and fixed size buffers would have been completely out of place. Would it be too much to ask for some discussion of anonymous methods and lambda expressions though, particularly as lambda expressions really do make LINQ possible?
Maybe I’m being too harsh, looking at just C# 2 and 3 features (which is pretty much all my book does). Here are some things which would have counted as being missing had the book been published in 2002:
- Casting ("as" doesn’t count as a cast in my view – it’s an operator)
- Explicit interface implementation
- The conditional operator (x ? y : z)
- Hiding members with "new" instead of "override"
- The sealed modifier on methods (not just classes)
- ref/out/params modifiers for parameters
- continue, goto, and break (break is mentioned for switch/case, but only there)
Maybe going into the memory model would have been a bit much (without which volatile would be pretty pointless) but not to even mention threading (and lock) feels a little worrying – especially as Application.DoEvents is abused instead. Explicit interface implementation is relatively obscure – but readonly fields? typeof(…)?
Fine, it’s an introductory text – but that means it’s a bad idea to talk about "mastering" LINQ and claiming that "by the time you’re through you’ll be a proficient C# programmer, designing and coding large-scale applications". Those quotes probably aren’t the authors’ fault, to be honest – and marketing has a way of exaggerating things, as we all know. But no-one should be in any doubt that this is far from a complete guide to C#.
Errors (please see update at the end of the post, and note at top)
This is by far my biggest gripe with the book. It’s probably coloured the whole review – things which I could have forgiven otherwise have been judged more harshly than they would have been if the entire book had been accurate. Accuracy is what I demand from a book above all else, partly because it’s not obvious to the reader when it’s absent. No-one could reasonably read the book without realising that they’re getting a top-down approach, or what the formatting is like, or that there are going to be crosswords etc. However, a reader has little to benchmark accuracy against unless the book is internally consistent.
Now, I’m unfortunate enough to have the first edition (November 2007). The book is currently undergoing its third printing, i.e. it’s had two rounds of corrections. These are listed in the errata and I’ve tried to take them into account – although there’s no way that a carefully reviewed and edited book should need that many corrections in such a short space of time. I will concede that a Head First book is likely to be much harder to get right than a "normal" book though, due to all the funky formatting.
Typos don’t worry me too much. It’s core technical errors which really bother me. It’s almost as if someone had taken my list of classic "myths" and decided to taunt me. I half expected "objects are passed by reference" to be in there – but as ref/out parameters aren’t covered at all, it’s not. Here are some of the worst/most amusing culprits though:
- Claiming that string is a value type (in several places). Oh, and object, once.
- Claiming that the range of sbyte is -127 to 128 (instead of -128 to 127). Same kind of mistake with short.
- Constantly using field and property as if they were interchangable. They’re not, they’re really, really not. Just because they’re used in a similar way doesn’t mean you can be this loose with the terminology.
- Claiming that C# "marks objects for garbage collection". In fact, for the first 6/7ths of the book there’s a strong implication that garbage collection is done in a deterministic way; that objects are immediately collected when the last reference is lost. We do eventually find out that it’s non-deterministic (although that explanation is also flawed) but by then it may well be too later for the reader. More on this in a minute.
- Claiming that methods and statements always have to live in classes. Funny how structs can have behaviour too…
- Claiming that "objects are variables" (in a heading, no less). I know from experience that trying to accurately describe the interplay between objects, variables, and their values is tricky – but even so…
- Writing a hex dump utility using StreamReader – broken by definition, given that hex dump tools are used to show the binary contents of files, and StreamReader is meant to read text, decoding it as it goes.
- Claiming that structs always live on the stack.
- Expanding WPF as "Windows Presentation Framework"
These are all errors which have made it through not just technical review, but two rounds of post-publication editing. It’s possible that some of the errors on my list of about 60 (ignoring typos for the most part) are in the errata and I missed them (I did try to check them all) – but really, I shouldn’t have been able to find that many in the first place, even if they have been corrected. I’m worried if a C# book author believes that a char has 256 possible values, for instance. I know that the author is wrong, and to check the errata (this one has indeed been fixed) but I suspect many first edition readers will never look at the errata.
Now, there are errors and there are bad practices…
Bad practice through example
I know we don’t end up writing production code as book examples. Indeed, Eric mentioned this in chapter 1, where I’d left a few things out which I would normally consider as best practice: making a type sealed, making fields readonly etc. I left extra modifiers out for simplicity. I can understand that. I can also understand using public fields until properties have been explained but:
- There’s no reason to use poorly named variables/parameters, including Pascal-cased local variables
- Writing loops like for (int i=1; i <= 10; i++) instead of the more idiomatic for (int i=0; i < 10; i++). C# is 0-based in many ways, but often the authors seemed to really wish it were 1-based.
- Continuing to use public fields even after explaining how they’re not really a good idea. (Well, sort of explaining that. There’s a frequent implication that they’re not so bad if other classes really need to be able to access your data. It’s a very long way from my preferred policy of no non-private variables whatsoever.)
- String concatenation in loops with nary a mention of StringBuilder
- Bizarre combination of "is" and "as", using "as" to perform the conversion instead of casting. If you’re going to use "as", do it up front and compare with null to start with…
- Advising leaving out braces for single line for/if statements. The code which is left in these examples is unreadable, IMO. You have to really concentrate to see what’s in and what’s out.
- Advising to stick with the absolutely abhorrent "convention" (aka laziness, and leaving things as VS creates them) of naming event handlers with things like button1_Click. No. Name methods with what they do, then the event hookup code will make it obvious – and it makes it clearer where you can reuse a single method for multiple events.
- Repeatedly declaring enums within classes as if you couldn’t write them as top-level types. Oh, and messing up the naming conventions there, too.
- Showing a mutable struct without explaining that this should always be avoided is a bad idea.
I could go on. I’ve got pages of notes about this kind of thing (and this is only after owning the book for less than a day, don’t forget) but I think you get the message. Note: some of these things are definitely a matter of opinion, such as bracing style. Some other things are so widely regarded as a bad idea that I can’t see much defence for them.
Examples matter. I don’t expect to see production code, and I understand that sometimes for teaching purposes best practices will take second place – but where it wouldn’t hurt to use best practice, please do!
Likewise telling the truth from the start matters. It’s very hard to correct bad habits and incorrect impressions. If you state that "When we set lucky (a variable) to null, it’s no longer pointing at its object, so it gets garbage collected" then people will not only be potentially confused about whether it’s the variable or the object which gets garbage collected, but they’ll get the impression that it’s garbage collected immediately. Waiting 476 pages to correct that impression is a bad idea.
Public fields are another example. I’ve mentioned that I can see their usefulness before we’ve encountered properties – but even so, surely it would have been worth explaining immediately that we’re going to hide them as soon as possible.
You may have gathered by now that I’m not a fan of the book ;) I suspect a lot of this review has come off as a rant, which is a pity. That tends to happen when I get on my technical high horse, which I guess I’ve done here. The fact is, I’ve spent a lot of today feeling deeply saddened. I wouldn’t be surprised to find that this is the best-selling C# book of 2008 – which means we’ll get a lot more people on the newsgroup with some very odd ideas about how C# works. That’s the trouble – I’ve seen what happens when people are fed the "structs live on the stack" myth. I’ve seen how easily people can believe that strings are value types, and that it doesn’t really matter if you use a StreamReader for binary data and then cast chars to bytes. It causes trouble.
I’m reasonably sure the world could do with a good introductory book on C#. HFC# has convinced me that such a book could be fun and have pretty pictures. But HFC# isn’t quite it. (And no, I don’t plan on writing it either.)
I gave an advance copy of this review to Andrew, who has replied remarkably politely and pleasantly (and quickly). He’s a true gent, giving a really thorough reply when I suspect most authors (perhaps including myself) would either have given short shrift to a review like this, or possibly ignored it competely and hoped it would go away.
He believes I was looking too much for a complete reference rather than an introductory text. I would say I wasn’t looking for completeness, but a better judge of what should be in a C# book (I’d have preferred ref/out to be covered, but would be happy to lose the section on GDI+ double buffering, for instance). I specifically don’t want an actual reference book if I’m going to recommend it to people to learn from – but I may be biased towards a reference style as that’s what I personally tend to learn from.
It was really the errors that affected this review more than anything though, and while a very few of them could be debated (whether an implicit reference conversion to a base class counts as upcasting, for instance – I’d only include explicit upcasting) many others are undeniable and really shouldn’t have made it through the review process. It’s possible that I’ll come back to HFC# in a year’s time and be more impressed by it, but I suspect the aversion to errors will overcome any mellowing towards the style :(
Update (22nd March 2008)
Andrew continues to amaze me in terms of taking this review in his stride. He’s now looking through the errors I found, and many should be fixed in the next printing. Bear in mind that without a lot of the errors, I would have had a more positive view from the start.
In short, I’m still not quite convinced I’d recommend the book, but my opinion has certainly mellowed (anticipating the error fixing, of course).