C# in Depth
Jon Skeet
  • April 2008
  • ISBN 9781933988368
  • 424 pages
This title is out of print and no longer for sale.

Become a C# 3 maestro!

Fabrice Marguerie, C# MVP author of LINQ in Action

Third edition of this book is available

C# in Depth is designed to bring you to a new level of programming skill. It dives deeply into key C# topics—in particular the new ones. You'll learn to reuse algorithms in a type-safe way with C# 2 generics and expand the functionality of existing classes and interfaces using C# 3 extension methods. Tricky issues become clear in author Jon Skeet's crisp, easy-to-follow explanations and snappy, pragmatic examples. With this book under your belt, you will easily learn—and then master—new frameworks and platforms.

Table of Contents show full

foreword

preface

acknowledgments

about this book

about the cover illustration

comments from the tech review

Part 1 Preparing for the journey

1. The changing face of C# development

1.1. Evolution in action: examples of code change

1.1.1. Defining the Product type

1.1.2. Sorting products by name

1.1.3. Querying collections

1.1.4. Representing an unknown price

1.1.5. LINQ and query expressions

1.2.1. The world before C#

1.2.2. C# and .NET are born

1.2.3. Minor updates with .NET 1.1 and the first major step: .NET 2.0

1.2.4. "Next generation" products

1.2.5. Historical perspective and the fight for developer support

1.3. The .NET platform

1.3.1. Distinguishing between language, runtime, and libraries

1.3.2. Untangling version number chaos

1.4. Fully functional code in snippet form

1.4.1. Snippets and their expansions

1.4.2. Introducing Snippy

1.5. Summary

2. Core foundations: building on C# 1

2.1. Delegates

2.1.1. A recipe for simple delegates

2.1.2. Combining and removing delegates

2.1.3. A brief diversion into events

2.1.4. Summary of delegates

2.2. Type system characteristics

2.2.1. C#'s place in the world of type systems

2.2.2. When is C#1’s type system not rich enough?

2.2.3. When does C#1’s type system get in the way?

2.2.4. Summary of type system characteristics

2.3. Value types and reference types

2.3.1. Values and references in the real world

2.3.2. Value and reference type fundamentals

2.3.3. Dispelling myths

2.3.4. Boxing and unboxing

2.3.5. Summary of value types and reference types

2.4. C# 2 and 3: new features on a solid base

2.5. Summary

Part 2 C# 2: solving the issues of C# 1

3. Parameterized typing with generics

3.1. Why generics are necessary

3.2. Simple generics for everyday use

3.2.1. Learning by example: a generic dictionary

3.2.2. Generic types and type parameters

3.2.3. Generic methods and reading generic declarations

3.3. Beyond the basics

3.3.1. Type constraints

3.3.2. Type inference for type arguments of generic methods

3.3.3. Implementing generics

3.4. Advanced generics

3.4.1. Static fields and static constructors

3.4.2. How the JIT compiler handles generics

3.4.3. Generic iteration

3.4.4. Reflection and generics

3.5. Generic collection classes in .NET 2.0

3.5.1. List<T>

3.5.2. Dictionary<TKey,TValue>

3.5.3. Queue<T> and Stack<T>

3.5.4. SortedList<TKey,TValue> and SortedDictionary<TKey,TValue>

3.5.5. LinkedList<T>

3.6. Limitations of generics in C# and other languages

3.6.1. Lack of covariance and contravariance

3.6.2. Lack of operator constraints or a "numeric" constraint

3.6.3. Lack of generic properties, indexers, and other member types

3.6.4. Comparison with C++ templates

3.6.5. Comparison with Java generics

3.7. Summary

4. Saying nothing with nullable types

4.1. What do you do when you just don’t have a value?

4.1.1. Why value type variables can’t be null

4.1.2. Patterns for representing null values in C#1

4.2. System.Nullable and System.Nullable

4.2.1. Introducing Nullable<T>

4.2.2. Boxing and unboxing

4.2.3. Equality of Nullable<T> instances

4.2.4. Support from the nongeneric Nullable class

4.3. C# 2’s syntactic sugar for nullable types

4.3.1. The ? modifier

4.3.2. Assigning and comparing with null

4.3.3. Nullable conversions and operators

4.3.4. Nullable logic

4.3.5. The null coalescing operator

4.4. Novel uses of nullable types

4.4.1. Trying an operation without using output parameters

4.4.2. Painless comparisons with the null coalescing operator

4.5. Summary

5. Fast-tracked delegates

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.4.1. Starting simply: acting on a parameter

5.4.2. Returning values from anonymous methods

5.4.3. Ignoring delegate parameters

5.5. Capturing variables in anonymous methods

5.5.1. Defining closures and different types of variables

5.5.2. Examining the behavior of captured variables

5.5.3. What’s the point of captured variables?

5.5.4. The extended lifetime of captured variables

5.5.5. Local variable instantiations

5.5.6. Mixtures of shared and distinct variables

5.5.7. Captured variable guidelines and summary

5.6. Summary

6. Implementing iterators the easy way

6.1. C# 1: the pain of handwritten iterators

6.2. C# 2: simple iterators with yield statements

6.2.1. Introducing iterator blocks and yield return

6.2.2. Visualizing an iterator’s workflow

6.2.3. Advanced iterator execution flow

6.2.4. Quirks in the implementation

6.3. Real-life example: iterating over ranges

6.3.1. Iterating over the dates in a timetable

6.3.2. Scoping the Range class

6.3.3. Implementation using iterator blocks

6.4. Pseudo-synchronous code with the Concurrency and Coordination Runtime

6.5. Summary

7. Concluding C# 2: the final features

7.1. Partial types

7.1.1. Creating a type with multiple files

7.1.2. Uses of partial types

7.1.3. Partial methods—C#3 only!

7.2. Static classes

7.3. Separate getter/setter property access

7.4. Namespace aliases

7.4.1. Qualifying namespace aliases

7.4.2. The global namespace alias

7.4.3. Extern aliases

7.5. Pragma directives

7.5.1. Warning pragmas

7.5.2. Checksum pragmas

7.6. Fixed-size buffers in unsafe code

7.7. Exposing internal members to selected assemblies

7.7.1. Friend assemblies in the simple case

7.7.2. Why use InternalsVisibleTo?

7.7.3. InternalsVisibleTo and signed assemblies

7.8. Summary

Part 3 C# 3—revolutionizing how we code

8. Cutting fluff with a smart compiler

8.1. Automatically implemented properties

8.2. Implicit typing of local variables

8.2.1. Using var to declare a local variable

8.2.2. Restrictions on implicit typing

8.2.3. Pros and cons of implicit typing

8.2.4. Recommendations

8.3. Simplified initialization

8.3.1. Defining our sample types

8.3.2. Setting simple properties

8.3.3. Setting properties on embedded objects

8.3.4. Collection initializers

8.3.5. Uses of initialization features

8.4. Implicitly typed arrays

8.5. Anonymous types

8.5.1. First encounters of the anonymous kind

8.5.2. Members of anonymous types

8.5.3. Projection initializers

8.5.4. What’s the point?

8.6. Summary

9. Lambda expressions and expression trees

9.1. Lambda expressions as delegates

9.1.1. Preliminaries: introducing the Func<…​> delegate types

9.1.2. First transformation to a lambda expression

9.1.3. Using a single expression as the body

9.1.4. Implicitly typed parameter lists

9.1.5. Shortcut for a single parameter

9.2. Simple examples using List and events

9.2.1. Filtering, sorting, and actions on lists

9.2.2. Logging in an event handler

9.3. Expression trees

9.3.1. Building expression trees programmatically

9.3.2. Compiling expression trees into delegates

9.3.3. Converting C# lambda expressions to expression trees

9.3.4. Expression trees at the heart of LINQ

9.4. Changes to type inference and overload resolution

9.4.1. Reasons for change: streamlining generic method calls

9.4.2. Inferred return types of anonymous functions

9.4.3. Two-phase type inference

9.4.4. Picking the right overloaded method

9.4.5. Wrapping up type inference and overload resolution

9.5. Summary

10. Extension methods

10.1. Life before extension methods

10.2. Extension method syntax

10.2.1. Declaring extension methods

10.2.2. Calling extension methods

10.2.3. How extension methods are found

10.2.4. Calling a method on a null reference

10.3. Extension methods in .NET 3.5

10.3.1. First steps with Enumerable

10.3.2. Filtering with Where, and chaining method calls together

10.3.3. Projections using the Select method and anonymous types

10.3.4. Sorting using the OrderBy method

10.3.5. Business examples involving chaining

10.4. Usage ideas and guidelines

10.4.1. "Extending the world" and making interfaces richer

10.4.2. Fluent interfaces

10.4.3. Using extension methods sensibly

10.5. Summary

11. Query expressions and LINQ to Objects

11.1. Introducing LINQ

11.1.1. What’s in a name?

11.1.2. Fundamental concepts in LINQ

11.1.3. Defining the sample data model

11.2. Simple beginnings: selecting elements

11.2.1. Starting with a source and ending with a selection

11.2.2. Compiler translations as the basis of query expressions

11.2.3. Range variables and nontrivial projections

11.2.4. Cast, OfType, and explicitly typed range variables

11.3. Filtering and ordering a sequence

11.3.1. Filtering using a where clause

11.3.2. Degenerate query expressions

11.3.3. Ordering using an orderby clause

11.4. Let clauses and transparent identifiers

11.4.1. Introducing an intermediate computation with let

11.4.2. Transparent identifiers

11.5. Joins

11.5.1. Inner joins using join clauses

11.5.2. Group joins with join …​ into clauses

11.5.3. Cross joins using multiple from clauses

11.6. Groupings and continuations

11.6.1. Grouping with the group …​ by clause

11.6.2. Query continuations

11.7. Summary

12. LINQ beyond collections

12.1. LINQ to SQL

12.1.1. Creating the defect database and entities

12.1.2. Populating the database with sample data

12.1.3. Accessing the database with query expressions

12.1.4. Updating the database

12.1.5. LINQ to SQL summary

12.2. Translations using IQueryable and IQueryProvider

12.2.2. Faking it: interface implementations to log calls

12.2.3. Gluing expressions together: the Queryable extension methods

12.2.4. The fake query provider in action

12.2.5. Wrapping up IQueryable

12.3. LINQ to DataSet

12.3.1. Working with untyped datasets

12.3.2. Working with typed datasets

12.4. LINQ to XML

12.4.1. XElement and XAttribute

12.4.2. Converting sample defect data into XML

12.4.3. Queries in LINQ to XML

12.4.4. LINQ to XML summary

12.5. LINQ beyond .NET 3.5

12.5.1. Third-party LINQ

12.5.2. Future Microsoft LINQ technologies

12.6. Summary

13. Elegant code in the new era

13.1. The changing nature of language preferences

13.1.1. A more functional emphasis

13.1.2. Static, dynamic, implicit, explicit, or a mixture?

13.2. Delegation as the new inheritance

13.3. Readability of results over implementation

13.4. Life in a parallel universe

13.5. Farewell

Appendix A: LINQ standard query operators

A.1. Aggregation

A.2. Concatenation

A.3. Conversion

A.4. Element operations

A.5. Equality operations

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 operations

A.14. Sorting

index

About the Technology

In programming, there's no substitute for knowing your stuff. In versions 2 and 3, C# introduces new concepts such as lambda expressions and implicit typing that make the language more flexible and give you more power. Using Language INtegrated Query (LINQ)—also new in C# 3—you can interact with data of any type directly from C#. Simply put, mastering these features will make you a more valuable C# developer.

What's inside

  • How and where (and why) to use the new language features
  • Backgrounder on C# 1
  • Cutting-edge best practices
  • Become comfortable and proficient with C# 2 and 3

About the author

Jon Skeet has worked with C# since 2002, and has been a Microsoft C# MVP since October 2003. He has spent a great amount of time in the C# community answering questions in newsgroups as well as writing articles on the most misunderstood aspects of C# and .NET. After having read tens of thousands of questions over the years, Jon has developed a deep insight into the areas that developers have trouble with, as well as what they are trying to achieve. A keen reader of specifications, Jon aims to understand the language at the deepest level, which enables him to provide a detailed exposition of C#, including a few dark corners which can trip up the unwary developer.