Table of Contents (3rd edition)
Here's a more detailed table of contents for the third edition of C# in Depth than the "about" page
can reasonably be expected to provide. This should tell you everything you need to know about what's in
the book, and what's not. If you still have any questions though, feel free to mail me and ask.
This is an introduction to the book - experienced developers could skip it completely if they really wanted to.
On the other hand, it gives an idea of what's coming up, as well as some hints and tips as to how to get the most out of
Chapter 1: The changing face of C# development
This is mostly a roadmap for the rest of the book - a quick demonstration of some of the features in C# 2, 3 and 4. Additionally
I explain how I differentiate between C# the language, the CLR as the virtual machine, and the various libraries that make up .NET. Finally
there are a couple of bits of housekeeping - a recommendation to download the C# language specification
and a warning about using teaching-aid code in production.
- 1.1 Starting with a simple data type
- 1.2 Sorting and filtering
- 1.3 Handling an absence of data
- 1.4 Introducing LINQ
- 1.5 COM and dynamic typing
- 1.6 Writing asynchronous code without the heartache
- 1.7 Dissecting the .NET platform
- 1.8 Making your code super awesome
- 1.9 Summary
Chapter 2: Core foundations: bounding on C# 1
Although C# in Depth is devoted to explaining the features introduced in C# 2, 3 and 4, these only
make any kind of sense if you already understand C# 1. This chapter looks at three topics from
the first version which are often misunderstood by newcomers to C#, and which are crucial for later versions.
We also have a look at how these areas have evolved over time.
- 2.1 Delegates
- 2.2 Type system characteristics
- 2.3 Value types and reference types
- 2.4 Beyond C# 1: new features on a solid base
- 2.5 Summary
This part of the book covers everything introduced in C# 2, and one new feature from C# 3 (partial methods).
By now the topics covered here are reasonably well known, but there are probably surprises for all
but the hardiest of C# developers.
Chapter 3: Parameterized typing with generics
As the most important feature in C# 2, generics stand out as a feature which would have been even
more wonderful had it been available right from the start. Much of C# as a language and .NET as a platform
might be somewhat more elegant if they hadn't had to work pre-generics. Many of the later language features
would be pretty much impossible without generics, and it's frankly hard to remember the language without them.
- 3.1 Why generics are necessary
- 3.2 Simple generics for everyday use
- 3.3 Beyond the basics
- 3.4 Advanced generics
- 3.5 Limitations of generics in C# and other languages
- 3.6 Summary
Chapter 4: Saying nothing with nullable types
Love 'em or hate 'em, null values are a fact of life in C#. But until C# 2, we could only use
them for reference types. What does it mean for a
byte variable to have a value of
null, anyway? Isn't
null a reference? Well, not any more - at least,
not always. .NET 2 introduced the
Nullable<T> value type - a wrapper which allows
a special value of "there's no real value here" - just like a null reference, in essence. In this
chapter we delve into all the details.
- 4.1 What do you do when you just don't have a value?
- 4.2 System.Nullable<T> and System.Nullable
- 4.3 C# 2's syntactic sugar for nullable types
- 4.4 Novel uses of nullable types
- 4.5 Summary
Chapter 5: Fast-tracked delegates
Ask a C# 1 programmer what delegates are used for, and they'd probably tell you they're pretty
much all about UI events. Maybe occasionally starting a thread, or an asynchronous callback. As time has gone on,
delegates have become more and more important in C#. They're a natural way of representing little bits of behaviour,
such as the filter or projection to apply in LINQ. In C# 1 they were somewhat awkward to use at times: the behaviour
had to be encapsulated in a method, and if that method needed any information other than what was exposed in the parameters,
it had to be present in the object the method was called on.
C# 2 fixed a lot of this with anonymous methods, and C# 3 slimmed them down even further with lambda expressions.
This chapter is primarily about anonymous methods and their subtleties, although there are a few other delegate-related
features in C# 2 as well.
- 5.1 Saying goodbye to awkward delegate syntax
- 5.2 Method group conversions
- 5.3 Covariance and contravariance
- 5.4 Inline delegate actions with anonymous methods
- 5.5 Capturing variables in anonymous methods
- 5.6 Summary
Chapter 6: Implementing iterators the easy way
Sequences are really useful. So useful in fact, they're the underlying data model of LINQ to Objects.
It's easy to use them - C# has always had the
foreach statement. But what about creating
them in the first place? It's fine if you've already got a list or an array, but how about modelling the Fibonacci
sequence, or lines read from a text file? That used to be somewhat tricky... but not any more.
- 6.1 C# 1: the pain of handwritten iterators
- 6.2 C# 2: simple iterators with yield statements
- 6.3 Real-life iterator examples
- 6.4 Pseudo-synchronous code with the Concurrency and Coordination Runtime
- 6.5 Summary
Chapter 7: Concluding C# 2: the final features
This is the first "grab-bag" chapter of features... essentially the ones that
aren't big enough to deserve a chapter to themselves. Some are only useful in emergencies
- others are very handy for everyday use.
- 7.1 Partial types
- 7.2 Static classes
- 7.3 Separate getter/setter property access
- 7.4 Namespace aliases
- 7.5 Pragma directives
- 7.6 Fixed-size buffers
- 7.7 Exposing internal members to selected assemblies
- 7.8 Summary
Even C# 3 feels somewhat "old" to me now (as I write this in 2010). However, it's where
C# starts to really feel like a modern language - one which lets me express myself with a lot
less ceremony than before. With LINQ at its heart, C# 3 makes working with collections of data a breeze -
but there's more to it than that.
Chapter 8: Cutting fluff with a smart compiler
Another variety chapter, this introduces several new features. Most of these contribute to LINQ
in some way or other, and while they are certainly distinct they feel more closely related than
the features we examined in chapter 7.
- 8.1 Automatically implemented properties
- 8.2 Implicit typing of local variables
- 8.3 Simplified initialization
- 8.4 Implicitly typed arrays
- 8.5 Anonymous types
- 8.6 Summary
Chapter 9: Lambda expressions and expression trees
Lambda expressions are like anonymous methods taken a step further: they allow you to create
delegate instances more concisely than ever before. They also allow you to create
expression trees - a representation of code as data. This is the secret sauce that allows
LINQ to SQL and its friends to examine your queries written in C# and translate them into SQL.
We examine both in this chapter. Oh, and type inference too. It's painful, but it has to be done.
- 9.1 Lambda expressions as delegates
- 9.2 Simple examples using List<T> and events
- 9.3 Expression trees
- 9.4 Changes to type inference and overload resolution
- 9.5 Summary
Chapter 10: Extension methods
Extension methods are static methods in static classes which are made to look like
instance methods on completely different types. Is that clear enough? Well, maybe not.
It sounds mad, but it's a really useful idea... especially when it comes to interfaces.
- 10.1 Life before extension methods
- 10.2 Extension method syntax
- 10.3 Extension methods in .NET 3.5
- 10.4 Usage ideas and guidelines
- 10.5 Summary
Chapter 11: Query expressions and LINQ to Objects
This is the big payoff for everything in part three so far... we're finally at LINQ.
In fact, by now there's only one missing piece - query expressions. These somewhat SQL-looking
pieces of code are cunningly translated by the C# compiler into perfectly normal bits of C# - which are then
compiled using the normal rules. The result is a small but powerful query language. This chapter goes
into the details of how query expressions are translated by the compiler, as well as showing what the
various parts do in LINQ to Objects.
- 11.1 Introducing LINQ
- 11.2 Simple beginnings: selecting elements
- 11.3 Filtering and ordering a sequence
- 11.4 "let" clauses and transparent identifiers
- 11.5 Joins
- 11.6 Groupings and continuations
- 11.7 Choosing between query expressions and dot notation
- 11.8 Summary
Chapter 12: LINQ beyond collections
By this point we've seen everything in the language itself, but we haven't really got a good
feel for LINQ. How flexible is it? Why isn't it specified in terms of normal interfaces? What
are expression trees really there for? This chapter looks at several LINQ APIs very briefly - just enough
to show you the breadth of what LINQ can achieve. We also look at how you can write your own extension
IEnumerable<T> to extend LINQ to Objects.
- 12.1 Querying a database with LINQ to SQL
- 12.2 Translations using IQueryable and IQueryProvider
- 12.3 LINQ-friendly APIs and LINQ to XML
- 12.4 Replacing LINQ to Objects with Parallel LINQ
- 12.5 Inverting the query model with LINQ to Rx
- 12.6 Extending LINQ to Objects
- 12.7 Summary
Compared with previous versions, C# 4 only contains a relatively small number of changes.
One of them, however, is huge - dynamic typing helps C# interoperate with other languages
Chapter 13: Minor changes to simplify code
As you might have guessed from the title, this is another chapter of small-ish features.
However, don't underestimate them: I suspect that personally I'll use some of these features
rather more than the behemoth of dynamic typing.
- 13.1 Optional parameters and named arguments
- 13.2 Improvements for COM interoperability
- 13.3 Generic variance for interfaces and delegates
- 13.4 Teeny tiny changes to locking and field-like events
- 13.5 Summary
Chapter 14: Dynamic binding in a static language
C# has always been static before. The compiler would only allow you to call methods which it
knew would be there. If you had more information than the compiler about the actual type of
an object, you had to cast the reference or something similar... then it would let you use
All of that changes in C# 4, but in a controlled way. I'm confident that most C# 4 developers
will use dynamic typing rarely if at all. However, if you want to work with COM or a dynamically
typed language such as IronRuby or IronPython, C# 4 makes it a smooth ride.
- 14.1 What? When? Why? How?
- 14.2 The 5 minute guide to
- 14.3 Examples of dynamic typing
- 14.4 Looking behind the scenes
- 14.5 Implementing dynamic behaviour
- 14.6 Summary
C# 5 has very few features: three really, and only one of those is particularly significant. However,
it really is a game-changer.
Chapter 15: Asynchrony with async/await
By far the biggest new feature in C# 5 is language support for asynchrony. C# 5 allows you to declare
asynchronous methods (and anonymous functions) which contain
await expressions which wait
for asynchronous operations to complete... but the waiting is performed asynchronously. It's hard to describe
in a single paragraph, but it's what the world's been waiting for when it comes to async code.
- 15.1 Introducing asynchronous functions
- 15.2 Thinking about asynchrony
- 15.3 Syntax and semantics
- 15.4 Asynchronous anonymous functions
- 15.5 Implementation details: compiler transformation
- 15.6 Using async/await effectively
- 15.7 Summary
Chapter 16: C# 5 bonus features and closing thoughts
The final chapter wraps up C# 5 by covering the two smaller features, and then considers the future briefly.
- 16.1 Changes to captured variables in foreach statements
- 16.2 Caller information attributes
- 16.3 Closing thoughts
C# in Depth is a language-focused book, but of course C# is almost always used in the context
of .NET. The appendices form a sort of quick reference for three aspects of .NET: LINQ, generic
collections, and the various versions of .NET available.
Appendix A: LINQ standard query operators
Although different providers will make different query operators available, the idea is that
they should work mostly the same in terms of results. This appendix looks at all the
standard query operators and how they work in LINQ to Objects in particular, including optimizations
- A.1 Aggregation
- A.2 Concatenation
- A.3 Conversion
- A.4 ELement operators
- A.5 Equality
- A.6 Generation
- A.7 Grouping
- A.8 Joins
- A.9 Partitioning
- A.10 Projection
- A.11 Quantifiers
- A.12 Filtering
- A.13 Set-based operators
- A.14 Sorting
Appendix B: Generic collections in .NET
Can't remember the difference between a
SortedList<TKey, TValue> and
SortedDictionary<TKey, TValue>? Not sure
whether to use an
IList<T> or an
If so, this appendix is for you.
- B.1 Interfaces
- B.2 Lists
- B.3 Dictionaries
- B.4 Sets
- B.6 Concurrent collections (.NET 4)
- B.7 Summary
Appendix C: Version summaries
The versions involved in .NET are confusing - particularly as .NET 3.0 doesn't contain
C# 3, for example. Introduce Silverlight, the Compact Framework and other editions into the mix,
and it's all a bit tricky. Hopefully this appendix will make things clearer...
- C.1 Desktop framework major releases
- C.2 C# language features
- C.3 Framework library features
- C.4 Runtime (CLR) features
- C.5 Related frameworks
- C.6 Summary