You are previewing C# in Depth.
O'Reilly logo
C# in Depth

Book Description

C# in Depth is a completely new book designed to propel existing C# developers to a higher level of programming skill. One simple principle drives this book: explore a few things deeply rather than offer a shallow view of the whole C# landscape. If you often find yourself wanting just a little more at the end of a typical chapter, this is the book for you. Expert author Jon Skeet dives into the C# language, plumbing new C# 2 and 3 features and probing the core C# language concepts that drive them. This unique book puts the new features into context of how C# has evolved without a lengthy rehearsal of the full C# language. C# in Depth briefly examines the history of C# and the .NET framework and reviews a few often-misunderstood C# 1 concepts that are very important as the foundation for fully exploiting C# 2 and 3. Because the book addresses C# 1 with a light touch, existing C# developers don't need to pick through the book in order to find new material to enhance their skills. This book focuses on the C# 2 and 3 versions of the language, but clearly explains where features are supported by changes in the runtime (CLR) or use new framework classes. Each feature gets a thorough explanation, along with a look on how you'd use it in real life applications. C# in Depth is both a vehicle for learning C# 2 and 3 and a reference work. Although the coverage is in-depth, the text is always accessible: You'll explore pitfalls that can trip you up, but you'll skip over gnarly details best left to the language specification. The overall effect is that readers become not just proficient in C# 2 and 3, but comfortable that they truly understand the language.

Table of Contents

  1. Copyright
    1. Dedication
  2. Foreword
  3. Preface
  4. Acknowledgments
  5. About This Book
    1. Who should read this book?
    2. Roadmap
    3. Terminology and typography
    4. Author Online and the C# in Depth website
    5. About the author
  6. About the Cover Illustration
  7. Comments from the Tech Review
  8. 1. Preparing for the journey
    1. 1. The changing face of C# development
      1. 1.1. Evolution in action: examples of code change
        1. 1.1.1. Defining the Product type
        2. 1.1.2. Sorting products by name
        3. 1.1.3. Querying collections
        4. 1.1.4. Representing an unknown price
        5. 1.1.5. LINQ and query expressions
      2. 1.2. A brief history of C# (and related technologies)
        1. 1.2.1. The world before C#
        2. 1.2.2. C# and .NET are born
        3. 1.2.3. Minor updates with .NET 1.1 and the first major step: .NET 2.0
        4. 1.2.4. “Next generation” products
        5. 1.2.5. Historical perspective and the fight for developer support
      3. 1.3. The .NET platform
        1. 1.3.1. Distinguishing between language, runtime, and libraries
          1. Language
          2. Runtime
          3. Framework Libraries
        2. 1.3.2. Untangling version number chaos
      4. 1.4. Fully functional code in snippet form
        1. 1.4.1. Snippets and their expansions
        2. 1.4.2. Introducing Snippy
      5. 1.5. Summary
    2. 2. Core foundations: building on C# 1
      1. 2.1. Delegates
        1. 2.1.1. A recipe for simple delegates
          1. Declaring the Delegate Type
          2. Finding an Appropriate Method for the Delegate Instance’s Action
          3. Creating a Delegate Instance
          4. Invoking a Delegate Instance
          5. A Complete Example and Some Motivation
        2. 2.1.2. Combining and removing delegates
        3. 2.1.3. A brief diversion into events
        4. 2.1.4. Summary of delegates
      2. 2.2. Type system characteristics
        1. 2.2.1. C#’s place in the world of type systems
          1. Static Typing vs. Dynamic Typing
          2. Explicit Typing vs. Implicit Typing
          3. Type-Safe vs. Type-Unsafe
        2. 2.2.2. When is C# 1’s type system not rich enough?
          1. Collections, Strongc and Weak
          2. Lack of Covariant Return Types
        3. 2.2.3. When does C# 1’s type system get in the way?
        4. 2.2.4. Summary of type system characteristics
      3. 2.3. Value types and reference types
        1. 2.3.1. Values and references in the real world
        2. 2.3.2. Value and reference type fundamentals
        3. 2.3.3. Dispelling myths
          1. Myth #1: “Structs are Lightweight Classes”
          2. Myth #2: “Reference Types Live on the Heap, Value Types Live on the Stack”
          3. Myth #3: “Objects are Passed by Reference in C# by Default”
        4. 2.3.4. Boxing and unboxing
        5. 2.3.5. Summary of value types and reference types
      4. 2.4. C# 2 and 3: new features on a solid base
        1. 2.4.1. Features related to delegates
        2. 2.4.2. Features related to the type system
        3. 2.4.3. Features related to value types
      5. 2.5. Summary
  9. 2. C#2: solving the issues of C#1
    1. 3. Parameterized typing with generics
      1. 3.1. Why generics are necessary
      2. 3.2. Simple generics for everyday use
        1. 3.2.1. Learning by example: a generic dictionary
        2. 3.2.2. Generic types and type parameters
        3. 3.2.3. Generic methods and reading generic declarations
      3. 3.3. Beyond the basics
        1. 3.3.1. Type constraints
          1. Reference Type Constraints
          2. Value Type Constraints
          3. Constructor Type Constraints
          4. Derivation Type Constraints
          5. Combining Constraints
        2. 3.3.2. Type inference for type arguments of generic methods
        3. 3.3.3. Implementing generics
          1. Default Value Expressions
          2. Direct Comparisons
          3. Full Comparison Example: Representing a Pair of Values
      4. 3.4. Advanced generics
        1. 3.4.1. Static fields and static constructors
        2. 3.4.2. How the JIT compiler handles generics
        3. 3.4.3. Generic iteration
        4. 3.4.4. Reflection and generics
          1. Using Typeof with Generic Types
          2. Methods and Properties of System.Type
          3. Reflecting Generic Methods
      5. 3.5. Generic collection classes in .NET 2.0
        1. 3.5.1. List<T>
          1. New Features of List<T>
          2. Features “Missing” from List<T>
        2. 3.5.2. Dictionary<TKey,TValue>
        3. 3.5.3. Queue<T> and Stack<T>
        4. 3.5.4. SortedList<TKey, TValue> and SortedDictionary<TKey,TValue>
        5. 3.5.5. LinkedList<T>
      6. 3.6. Limitations of generics in C# and other languages
        1. 3.6.1. Lack of covariance and contravariance
          1. Why Don’T Generics Support Covariance?
          2. Where Covariance Would be Useful
          3. Where Contravariance Would be Useful
        2. 3.6.2. Lack of operator constraints or a “numeric” constraint
        3. 3.6.3. Lack of generic properties, indexers, and other member types
        4. 3.6.4. Comparison with C++ templates
        5. 3.6.5. Comparison with Java generics
      7. 3.7. Summary
    2. 4. Saying nothing with nullable types
      1. 4.1. What do you do when you just don’t have a value?
        1. 4.1.1. Why value type variables can’t be null
        2. 4.1.2. Patterns for representing null values in C# 1
          1. Pattern 1: The Magic Value
          2. Pattern 2: A Reference Type Wrapper
          3. Pattern 3: An Extra Boolean Flag
      2. 4.2. System.Nullable<T> and System.Nullable
        1. 4.2.1. Introducing Nullable<T>
        2. 4.2.2. Boxing and unboxing
        3. 4.2.3. Equality of Nullable<T> instances
        4. 4.2.4. Support from the nongeneric Nullable class
      3. 4.3. C# 2’s syntactic sugar for nullable types
        1. 4.3.1. The ? modifier
        2. 4.3.2. Assigning and comparing with null
        3. 4.3.3. Nullable conversions and operators
          1. Conversions Involving Nullable Types
          2. Operators Involving Nullable Types
        4. 4.3.4. Nullable logic
        5. 4.3.5. The null coalescing operator
      4. 4.4. Novel uses of nullable types
        1. 4.4.1. Trying an operation without using output parameters
        2. 4.4.2. Painless comparisons with the null coalescing operator
      5. 4.5. Summary
    3. 5. Fast-tracked delegates
      1. 5.1. Saying goodbye to awkward delegate syntax
      2. 5.2. Method group conversions
      3. 5.3. Covariance and contravariance
      4. 5.4. Inline delegate actions with anonymous methods
        1. 5.4.1. Starting simply: acting on a parameter
        2. 5.4.2. Returning values from anonymous methods
        3. 5.4.3. Ignoring delegate parameters
      5. 5.5. Capturing variables in anonymous methods
        1. 5.5.1. Defining closures and different types of variables
        2. 5.5.2. Examining the behavior of captured variables
        3. 5.5.3. What’s the point of captured variables?
        4. 5.5.4. The extended lifetime of captured variables
        5. 5.5.5. Local variable instantiations
        6. 5.5.6. Mixtures of shared and distinct variables
        7. 5.5.7. Captured variable guidelines and summary
          1. Guidelines for Using Captured Variables
      6. 5.6. Summary
    4. 6. Implementing iterators the easy way
      1. 6.1. C#1: the pain of handwritten iterators
      2. 6.2. C#2: simple iterators with yield statements
        1. 6.2.1. Introducing iterator blocks and yield return
        2. 6.2.2. Visualizing an iterator’s workflow
        3. 6.2.3. Advanced iterator execution flow
          1. Ending an Iterator with Yield Break
          2. Execution of Finally Blocks
        4. 6.2.4. Quirks in the implementation
      3. 6.3. Real-life example: iterating over ranges
        1. 6.3.1. Iterating over the dates in a timetable
        2. 6.3.2. Scoping the Range class
        3. 6.3.3. Implementation using iterator blocks
      4. 6.4. Pseudo-synchronous code with the Concurrency and Coordination Runtime
      5. 6.5. Summary
    5. 7. Concluding C# 2: the final features
      1. 7.1. Partial types
        1. 7.1.1. Creating a type with multiple files
        2. 7.1.2. Uses of partial types
        3. 7.1.3. Partial methods—C# 3 only!
      2. 7.2. Static classes
      3. 7.3. Separate getter/setter property access
      4. 7.4. Namespace aliases
        1. 7.4.1. Qualifying namespace aliases
        2. 7.4.2. The global namespace alias
        3. 7.4.3. Extern aliases
      5. 7.5. Pragma directives
        1. 7.5.1. Warning pragmas
        2. 7.5.2. Checksum pragmas
      6. 7.6. Fixed-size buffers in unsafe code
      7. 7.7. Exposing internal members to selected assemblies
        1. 7.7.1. Friend assemblies in the simple case
        2. 7.7.2. Why use InternalsVisibleTo?
        3. 7.7.3. InternalsVisibleTo and signed assemblies
      8. 7.8. Summary
  10. 3. C# 3—revolutionizing how we code
    1. 8. Cutting fluff with a smart compiler
      1. 8.1. Automatically implemented properties
      2. 8.2. Implicit typing of local variables
        1. 8.2.1. Using var to declare a local variable
        2. 8.2.2. Restrictions on implicit typing
        3. 8.2.3. Pros and cons of implicit typing
        4. 8.2.4. Recommendations
      3. 8.3. Simplified initialization
        1. 8.3.1. Defining our sample types
        2. 8.3.2. Setting simple properties
        3. 8.3.3. Setting properties on embedded objects
        4. 8.3.4. Collection initializers
          1. Creating New Collections with Collection Initializers
          2. Populating Collections Within Other Object Initializers
        5. 8.3.5. Uses of initialization features
          1. “Constant” Collections
          2. Setting Up Unit Tests
          3. Parameter Encapsulation
          4. <Insert Your Favorite Use Here>
      4. 8.4. Implicitly typed arrays
      5. 8.5. Anonymous types
        1. 8.5.1. First encounters of the anonymous kind
        2. 8.5.2. Members of anonymous types
        3. 8.5.3. Projection initializers
        4. 8.5.4. What’s the point?
      6. 8.6. Summary
    2. 9. Lambda expressions and expression trees
      1. 9.1. Lambda expressions as delegates
        1. 9.1.1. Preliminaries: introducing the Func<...> delegate types
        2. 9.1.2. First transformation to a lambda expression
        3. 9.1.3. Using a single expression as the body
        4. 9.1.4. Implicitly typed parameter lists
        5. 9.1.5. Shortcut for a single parameter
      2. 9.2. Simple examples using List<T> and events
        1. 9.2.1. Filtering, sorting, and actions on lists
        2. 9.2.2. Logging in an event handler
      3. 9.3. Expression trees
        1. 9.3.1. Building expression trees programmatically
        2. 9.3.2. Compiling expression trees into delegates
        3. 9.3.3. Converting C# lambda expressions to expression trees
        4. 9.3.4. Expression trees at the heart of LINQ
      4. 9.4. Changes to type inference and overload resolution
        1. 9.4.1. Reasons for change: streamlining generic method calls
        2. 9.4.2. Inferred return types of anonymous functions
        3. 9.4.3. Two-phase type inference
        4. 9.4.4. Picking the right overloaded method
        5. 9.4.5. Wrapping up type inference and overload resolution
      5. 9.5. Summary
    3. 10. Extension methods
      1. 10.1. Life before extension methods
      2. 10.2. Extension method syntax
        1. 10.2.1. Declaring extension methods
        2. 10.2.2. Calling extension methods
        3. 10.2.3. How extension methods are found
        4. 10.2.4. Calling a method on a null reference
      3. 10.3. Extension methods in .NET 3.5
        1. 10.3.1. First steps with Enumerable
        2. 10.3.2. Filtering with Where, and chaining method calls together
        3. 10.3.3. Projections using the Select method and anonymous types
        4. 10.3.4. Sorting using the OrderBy method
        5. 10.3.5. Business examples involving chaining
          1. Aggregation: Summing Salaries
          2. Grouping: Counting Bugs Assigned to Developers
      4. 10.4. Usage ideas and guidelines
        1. 10.4.1. “Extending the world” and making interfaces richer
        2. 10.4.2. Fluent interfaces
        3. 10.4.3. Using extension methods sensibly
      5. 10.5. Summary
    4. 11. Query expressions and LINQ to Objects
      1. 11.1. Introducing LINQ
        1. 11.1.1. What’s in a name?
        2. 11.1.2. Fundamental concepts in LINQ
          1. Sequences
          2. Deferred Execution and Streaming
          3. Standard Query Operators
        3. 11.1.3. Defining the sample data model
      2. 11.2. Simple beginnings: selecting elements
        1. 11.2.1. Starting with a source and ending with a selection
        2. 11.2.2. Compiler translations as the basis of query expressions
        3. 11.2.3. Range variables and nontrivial projections
        4. 11.2.4. Cast, OfType, and explicitly typed range variables
      3. 11.3. Filtering and ordering a sequence
        1. 11.3.1. Filtering using a where clause
        2. 11.3.2. Degenerate query expressions
        3. 11.3.3. Ordering using an orderby clause
      4. 11.4. Let clauses and transparent identifiers
        1. 11.4.1. Introducing an intermediate computation with let
        2. 11.4.2. Transparent identifiers
      5. 11.5. Joins
        1. 11.5.1. Inner joins using join clauses
        2. 11.5.2. Group joins with join ... into clauses
        3. 11.5.3. Cross joins using multiple from clauses
      6. 11.6. Groupings and continuations
        1. 11.6.1. Grouping with the group ... by clause
        2. 11.6.2. Query continuations
      7. 11.7. Summary
    5. 12. LINQ beyond collections
      1. 12.1. LINQ to SQL
        1. 12.1.1. Creating the defect database and entities
          1. Creating the SQL Server 2005 Database Schema
          2. Creating the Entity Classes
        2. 12.1.2. Populating the database with sample data
        3. 12.1.3. Accessing the database with query expressions
          1. First Query: Finding Defects Assigned to Tim
          2. SQL Generation for a More Complex Query: A Let Clause
          3. Explicit Joins: Matching Defects with Notification Subscriptions
          4. Implicit Joins: Showing Defect Summaries and Project Names
        4. 12.1.4. Updating the database
        5. 12.1.5. LINQ to SQL summary
      2. 12.2. Translations using IQueryable and IQueryProvider
        1. 12.2.1. Introducing IQueryable<T> and related interfaces
        2. 12.2.2. Faking it: interface implementations to log calls
        3. 12.2.3. Gluing expressions together: the Queryable extension methods
        4. 12.2.4. The fake query provider in action
        5. 12.2.5. Wrapping up IQueryable
      3. 12.3. LINQ to DataSet
        1. 12.3.1. Working with untyped datasets
        2. 12.3.2. Working with typed datasets
          1. Creating the Typed Dataset with Visual Studio
          2. Populating and Querying Typed Datasets
      4. 12.4. LINQ to XML
        1. 12.4.1. XElement and XAttribute
        2. 12.4.2. Converting sample defect data into XML
        3. 12.4.3. Queries in LINQ to XML
        4. 12.4.4. LINQ to XML summary
      5. 12.5. LINQ beyond .NET 3.5
        1. 12.5.1. Third-party LINQ
          1. LINQ to Amazon
          2. LINQ to Nhibernate
          3. LINQ to Active Directory
        2. 12.5.2. Future Microsoft LINQ technologies
          1. The ADO.NET Entity Framework
          2. Parallel LINQ (PLINQ)
      6. 12.6. Summary
    6. 13. Elegant code in the new era
      1. 13.1. The changing nature of language preferences
        1. 13.1.1. A more functional emphasis
        2. 13.1.2. Static, dynamic, implicit, explicit, or a mixture?
      2. 13.2. Delegation as the new inheritance
      3. 13.3. Readability of results over implementation
      4. 13.4. Life in a parallel universe
      5. 13.5. Farewell
  11. LINQ standard query operators
    1. A.1. Aggregation
    2. A.2. Concatenation
    3. A.3. Conversion
    4. A.4. Element operations
    5. A.5. Equality operations
    6. A.6. Generation
    7. A.7. Grouping
    8. A.8. Joins
    9. A.9. Partitioning
    10. A.10. Projection
    11. A.11. Quantifiers
    12. A.12. Filtering
    13. A.13. Set-based operations
    14. A.14. Sorting