Effective C# 233
Effective C#: 50 Specific Ways to Improve Your C# | |
author | Bill Wagner |
pages | 336 |
publisher | Addison Wesley |
rating | 9 |
reviewer | Jim Holmes |
ISBN | 0321245660 |
summary | Must-have addition to any serious C# developer's bookshelf |
If you're interested in, or currently working with, .NET and are tempted to skip past this book as Just Another C# Reference Book, think again. Wagner's book is a great resource because it covers concepts which run across the entire .NET Framework regardless of which language you're working with. While this book focuses on C#, VB.NET developers can benefit from some of the text within as well.
I also think this book speaks to a wide range of readers. Seasoned developers will blow through this content, fine-tuning their coding methods or starting new ones. Wagner specifically points out how practices experienced C++ developers may use aren't good practices in C#, such as avoiding function call overhead by writing longer C# methods with more convoluted loops. More on that later.
New developers also can greatly benefit from this book by using it to properly form development habits early in their careers. Examples of this might include following Wagner's recommendations for safe casting, strong use of interfaces, and knowledgeable resource management.
Wagner's writing style is clear and concise. He occasionally comes across as brusque, or as writing only to experienced developers ("I wouldn't write code like this and neither would you."), but those instances are few and far between. The rest of the book's voice is terrific. More important is the weight of Wagner's knowledge and experience.
One real drawback is a large number of typographical errors, sometimes several per chapter. Some sentences are missing content, and there are a large number of run-together words. These errors don't take away from the material, but it's an annoyance all the same. I would have expected better proofreaders at AW.
The book is well organized into six chapters, each hitting a specific area in C#. Within each chapter, Wagner covers six to ten items, each item focusing on one specific "minitopic," as Wagner calls them. Each item includes code snippets to demonstrate recommended approaches. Few of Wagner's snippets will function as standalone programs, but this is an advantage, as I see it. The book focuses on tight, specific examples, rather than weighing itself down with pages of extraneous fluff.
Often Wagner's recommended approach is contrasted against bad practices, or practices which might be optimal in other languages but work poorly in C#. An example of this would be Chapter 4's Item 31: "Prefer Small, Simple Functions," where Wagner shows how smaller functions are generally more efficient than larger functions with complex loops. This probably confounds experienced C++ developers, but it's a prime example of how valuable this book is. Wagner shows that .NET's Just-In-Time compiler pays less cost when calling functions than it does trying to wade through convoluted loop logic. His recommendation? Write "the clearest code you can create. Let the JIT compiler do the rest."
Chapter 1, "C# Language Elements", hits hard the topics "you must remember every day when you write C# code." This chapter discusses issues central to C#'s syntax, implementation and optimization. Wagner talks about basic Object Oriented concepts such as hiding class data members behind Properties (.NET's common access methods/fields via gets and sets), and why it's important to implement a ToString() method. Basic software engineering topics are also covered, like why it's important to differentiate between value and reference data types -- and the pitfalls of failing to do so. This chapter also thrashes out coverage on deep C# concepts like why developers should use foreach loops and why the GetHashCode() method will "practically always get you in trouble."
Chapter 2, ".NET Resource Management", has a lot of text on general patterns for constructing optimal code. Wagner's in-depth knowledge of the .NET Framework's underpinnings really shows through here. There's a very clear discussion of the performance ramifications of boxing (wrapping value type data into an object for method parameters) and unboxing. Minimizing extra garbage (unnecessary objects) and easing resource clean up via standard dispose patterns are also covered. This chapter's critical to ensuring you understand what's going on with resources in your .NET application.
Chapter 3, "Expressing Designs with C#", looks at object-oriented design in C#. While the discussion's specific to C#, there's a lot of great, practical information which applies to any object oriented development. Wagner gives some great examples with backup discussion regarding preferring the use of interfaces over inheritance and why it's a cleaner solution. (Java programmers who've read Alan Holub's "Why extends is Evil" in JavaWorld would enjoy this section.) There's also great treatment of using delegates for callbacks, and events for outgoing interfaces. Wagner also points out more pitfalls in a reference data type language: returning references to internal class objects via a read-only property (getter for Java folks).
Chapter 4, "Creating Binary Components", shows what critical topics you have to consider when creating even a moderately complex system for deployment. Wagner exposes some terrific details on how smaller is better when developing .NET assemblies for deployment. He also discusses why it's best to limit a class's exposure through public scope since this ends up advertising too much of your class's internals to potential users of that class. Wagner ties this back to interface discussions in earlier portions of the book, and makes a good case in this section for bad scope's impact on deployment.
Chapter 5, "Working with the Framework", delves into the .NET Framework Class Library. The FCL is a huge library and Wagner's insistent that too many developers are writing custom code for functionality which already exists in the FCL. This section helps to avoid having "developers reinvent the wheel." There are very useful discussions on using .NET runtime diagnostics, .NET's validation capabilities, and standard configuration mechanisms. Wagner also shows why .NET's reflection capability (one component dynamically discovering another component's capabilities at runtime) can be overused - but he also shows how to best use it in the appropriate cases.
Chapter 6, "Miscellaneous", is the catch-all section. Security and exceptions are covered here, as is the pain of working with COM interoperability - and why you should avoid it if at all possible. Just as importantly, Wagner points to several tools which should be in any C# developer's belt. He also identifies terrific resources available online.
What makes this book so useful is that Wagner constantly talks about the reasons behind why specific choices in C# should be made. For example, in Item 3, "Prefer the as and is Operator to Casts" Wagner moves through the rationale of why a developer should (when possible) avoid casting in C# and use the as and is operators instead. Casting can lead to headaches with data loss when casting a long data type to an integer one, or more headaches with the extra lines of code to ensure the cast was to a proper type. Sure, casts are sometimes necessary, and it's another value point for this book that Wagner gives clear examples of when his techniques don't apply -- and he also shows recommended alternatives for those cases.
It's just this kind of discussion from an experienced developer that makes this book so valuable. Good developers need to understand the ramifications of choices they make designing and implementing a system. Wagner's book is outstanding for exactly this kind of detailed, clear exposition.
An additional bonus: Wagner has a blog dedicated to discussion of items from his book. Erata are also listed there. See Bill Wagner's Effective C# blog.
The bottom line: this book really is a critical addition to a serious C# or .NET developer's bookshelf. It deserves a place right alongside books from McConnell, Macguire, Kernighan and Pike.
You can purchase Effective C#: 50 Specific Ways to Improve Your C# from bn.com. Slashdot welcomes readers' book reviews -- to see your own review here, read the book review guidelines, then visit the submission page.
Get a design patterns book (Score:2, Insightful)
from the oxymoron dept... (Score:2, Insightful)
[mod me +5 funny like the parent. What's that? You don't like underhanded my-langauge-is-better-than-yours jokes, meant to enflame rage between already volatile communities? Oh, sorry, mod me down then.]
Re:But does it... (Score:3, Insightful)
Re:Hah (Score:4, Insightful)
You don't like
Re:So Call Me Old And Cranky (Score:5, Insightful)
Someone may be an expert at high-level OO design, but when it comes to programming in C++ specifically, he might not know about things like the utility of auto_ptr, that it's a bad idea to throw an exception from a destructor, why destructors should often be virtual, dangerous cases where an unexpected temporary object might get created and changed instead of the intended one, etc... That was where the value of the Effective C++ books was for me.
large functions in c++ (Score:5, Insightful)
Often Wagner's recommended approach is contrasted against bad practices, or practices which might be optimal in other languages but work poorly in C#. An example of this would be Chapter 4's Item 31: "Prefer Small, Simple Functions," where Wagner shows how smaller functions are generally more efficient than larger functions with complex loops. This probably confounds experienced C++ developers, but it's a prime example of how valuable this book is. Wagner shows that
I don't know how he got the impression that experienced C++ developers would be confounded by short functions. By using short inlined functions, C++ developers don't pay any cost by breaking up large routines.
Re:So Call Me Old And Cranky (Score:3, Insightful)
And knowing the language. Sometimes there's idioms in a language that just aren't obvious. Like the use of "explicit" to prevent C++ from surprise autocasts, or what const correctness really means and how to avoid breaking it. Or when to explicitly null references in Java to get them gc'd, and when it hurts performance (this idiom changed when Java's gc algorithm changed).
C# no doubt has all sorts of odd wrinkles with respect to generics and delegates and whatnot, and likely it takes a book to list them all. There's knowing a language's syntax, and there's being really effective with it and working within its limitations. Scott Meyer's Effective C++ is one of the best books on C++ you can get, and it's certainly no introductory text. I've no idea whether this book is on that same plane, but all language books are most certainly not created equal, and if you think you're an expert because you happen to know the syntax, you're only as valuable as the next codemonkey with a training certificate.
Re:from the oxymoron dept... (Score:1, Insightful)
When performance is not critical important and you make decision to target a VM, you may as well use a dynamic typed language. Enjoy Ironpython.
You want funny? Microsoft should have called it !C or J#
Re:In the same class as code complete? (Score:1, Insightful)
I was beginning to think I had slipped into bizarro world. Don't get me wrong. Code Complete is probably a useful read for a beginner, but I hardly think of it as professional level.
It seemed to be just another book-by-the-pound that made someone lots of money for stating the obvious (at great length), mixed indifferently with tons of editorializing.
Now I only read the first edition. (I don't remember if I got all the way through, it was a few years ago) but there was nothing there that would make me want to look at a second edition.
orkstkk
Re:from the oxymoron dept... (Score:2, Insightful)
But its not an open platform if you've got a submarine patents laying around. I feel a bit superstitious about saying this - but I just don't trust Microsoft enough to be in control of the tools I use for my livelihood (programming for cash).
My attitude may change, but in the past my experience has shown me its not worth it.
Given that, I'd rather learn Ruby on Rails if I'm going to do sometehing new, really.
Re:from the oxymoron dept... (Score:2, Insightful)
So is C++. Why eliminate the standard programming language in favor of another? You know Microsoft didn't just develop it out of the goodness of their hearts. It was a business decision that will be good for Microsoft and bad for programmers. That is, unless you like learning yet another redundant programming language.
Visual Studio & .NET are getting better! (Score:2, Insightful)
end-disclaimer.
The MS bashing is quite disturbing. Well i know that
While i may have made myself flamebait, i think that if you are honestly interested in VS, you wont be disappointed by the new improvements they've made to Visual Studio.
Re:Design Patterns are the symptom of a problem (Score:3, Insightful)
The idea behind good patterns is to reduce common coding occurances into a generic pattern. Sort of a meta code reuse scheme.
Rather than focusing on rewriting a stack or queue from scratch, you just use something called a stack or queue (more generically, a "list") that functionally does the operations, i.e., push, pop, swap, count associated with what you want to use. You don't care if it's implemented as an array, a linked list of arrays, a hash table, etc. (and if you did, it could be the way you might want to implement it is not as good of a way to implement it in that particular instance).
SQL is in some sense a design pattern. Instead of everybody writing the same CRUD database code (like Foxpro programmers, and finding a zillion different implementations of the same thing), because always having to write loops over recordsets is a whole lot cooler than just "select * from blah, and anyone who relies on that SQL abomination crutch is weak-minded!", well, I think has seriously missed the boat. Same with patterns.
At the end of the day, all I really care is that I can store and retrieve data in an organized and flexible fashion without much effort, that is in and of itself very tolerant of design changes, and I don't have to worry about whether it's implented as a balanced B-Tree, a sorted binary tree, a red-black tree, a flat file, etc.
I can implment, say, an Observer pattern (i.e., grab some code off of a website) for a certain chunk of my code, and then I have a lot less to implement if I need to provide it for other classes of objects.
Patterns seem to allow the programmer to think conceptually about more abstract things. They are not supposed to be the end, but just a means to an end. No one *has* to use patterns.
But, like lists, some coding concepts just seem to happen more often than not, and, like writing a function library, on a more generic level we have 'patterns', and having a basic conceptual/pseudo-code 'pattern' to think about helps one think of other things faster, rather than fleshing out the details right now before I can go on, and having to massively redo it if the plan changes.
Object-oriented programming is a bit of a pattern in and of itself as well compared to procedural programming, which is a pattern for assembly programming, etc.