C# in Depth, Fourth Edition
Jon Skeet
  • MEAP began March 2017
  • Publication in January 2019 (estimated)
  • ISBN 9781617294532
  • 514 pages (estimated)
  • printed in black & white
free previous edition included

GET MORE WITH MANNING

An eBook copy of the previous edition, C# in Depth, Third Edition, is included at no additional cost. It will be automatically added to your Manning account within 24 hours of purchase.


C# is an amazing language that's up to any challenge you can throw at it. As a C# developer, you also need to be up to the task. C# in Depth, Fourth Edition is your key to unlocking the powerful new features added to the language in C# 5, 6, and 7. Following the expert guidance of C# legend Jon Skeet, you'll master asynchronous functions, expression-bodied members, interpolated strings, tuples, and much more.
Table of Contents detailed table of contents

Part 1: Introduction

1 Survival of the sharpest

1.1 An evolving language

1.1.1 A helpful type system at large and small scales

1.1.2 Ever more concise code

1.1.3 Simple data access with LINQ

1.1.4 Asynchrony

1.1.5 Balancing efficiency and complexity

1.1.6 Evolution at speed: using minor versions

1.2 An evolving platform

1.3 An evolving community

1.4 An evolving book

1.4.1 Mixed-level coverage

1.4.2 Examples using Noda Time

1.4.3 Terminology choices

1.5 Summary

Part 2: C# 2-4

2 C# 2

2.1 Generics

2.1.1 Introduction by example: collections before generics

2.1.2 Generics save the day

2.1.3 What can be generic?

2.1.4 Type inference for type arguments to methods

2.1.5 Type constraints

2.1.6 The default and typeof operators

2.1.7 Generic type initialization and state

2.2 Nullable value types

2.2.1 Aim: expressing an absence of information

2.2.2 CLR and Framework support: the Nullable<T> struct

2.2.3 Language support

2.3 Simplified delegate creation

2.3.1 Method group conversions

2.3.2 Anonymous methods

2.3.3 Delegate compatibility

2.4 Iterators

2.4.1 Introduction to iterators

2.4.2 Lazy execution

2.4.3 Evaluation of yield statements

2.4.4 The importance of being lazy

2.4.5 Evaluation of finally blocks

2.4.6 Implementation sketch

2.5 Minor features

2.5.1 Partial types

2.5.2 Static classes

2.5.3 Separate getter/setter access for properties

2.5.4 Namespace aliases

2.5.5 Pragma directives

2.5.6 Fixed-size buffers

2.5.7 InternalsVisibleTo

2.6 Summary

3 C# 3: LINQ and everything that comes with it

3.1 Automatically implemented properties

3.2 Implicit typing

3.2.1 Typing terminology

3.2.2 Implicitly typed local variables (var)

3.2.3 Implicitly typed arrays

3.3 Object and collection initializers

3.3.1 Introduction to object and collection initializers

3.3.2 Object initializers

3.3.3 Collection initializers

3.3.4 The benefits of single expressions for initialization

3.4 Anonymous types

3.4.1 Syntax and basic behavior

3.4.2 The compiler-generated type

3.4.3 Limitations

3.5 Lambda expressions

3.5.1 Lambda expression syntax

3.5.2 Capturing variables

3.5.3 Expression trees

3.6 Extension methods

3.6.1 Declaring an extension method

3.6.2 Invoking an extension method

3.6.3 Chaining method calls

3.7 Query expressions

3.7.1 Query expressions translate from C# to C#

3.7.2 Range variables and transparent identifiers

3.7.3 Deciding when to use which syntax for LINQ

3.8 The end result: LINQ

3.9 Summary

4 C# 4: improving interoperability

4.1 Dynamic typing

4.1.1 Introduction to dynamic typing

4.1.2 Dynamic behavior beyond reflection

4.1.3 A brief look behind the scenes

4.1.4 Limitations and surprises in dynamic typing

4.1.5 Usage suggestions

4.2 Optional parameters and named arguments

4.2.1 Parameters with default values, and arguments with names

4.2.2 Determining the meaning of a method call

4.2.3 Impact on versioning

4.3 COM interoperability improvements

4.3.1 Linking Primary Interop Assemblies

4.3.2 Optional parameters in COM

4.3.3 Named indexers

4.4 Generic variance

4.4.1 Simple examples of variance in action

4.4.2 Syntax for variance in interface and delegate declarations

4.4.3 Restrictions on using variance

4.4.4 Generic variance in practice

4.5 Summary

Part 3: C# 5

5 Writing asynchronous code

5.1 Introducing asynchronous functions

5.1.1 First encounters of the asynchronous kind

5.1.2 Breaking down the first example

5.2 Thinking about asynchrony

5.2.1 Fundamentals of asynchronous execution

5.2.2 Synchronization contexts

5.2.3 Modeling asynchronous methods

5.3 Async method declarations

5.3.1 Return types from async methods

5.3.2 Parameters in async methods

5.4 Await expressions

5.4.1 The awaitable pattern

5.4.2 Restrictions on await expressions

5.5 Wrapping of return values

5.6 Asynchronous method flow

5.6.1 What is awaited and when?

5.6.2 Evaluation of await expressions

5.6.3 The use of awaitable pattern members

5.6.4 Exception unwrapping

5.6.5 Method completion

5.7 Asynchronous anonymous functions

5.8 Custom task types in C# 7

5.8.1 The 99.9% case: ValueTask<TResult>

5.8.2 The 0.1% case: building your own custom task type

5.9 Async main methods in C# 7.1

5.10 Usage tips

5.10.1 Avoid context capture using ConfigureAwait (where appropriate)

5.10.2 Enable parallelism by starting multiple independent tasks

5.10.3 Avoid mixing synchronous and asynchronous code

5.10.4 Allow cancellation wherever possible

5.10.5 Testing asynchrony

5.11 Summary

6 Async implementation

6.1 Structure of the generated code

6.1.1 The stub method: preparation and taking the first step

6.1.2 Structure of the state machine

6.1.3 The MoveNext() method (high level)

6.1.4 The SetStateMachine method and the state machine boxing dance

6.2 A “simple” MoveNext() implementation

6.2.1 A full concrete example

6.2.2 MoveNext() method general structure

6.2.3 Zooming into an await expression

6.3 How control flow affects MoveNext()

6.3.1 Control flow between await expressions is simple

6.3.2 Awaiting within a loop

6.3.3 Awaiting within a try/finally block

6.4 Execution contexts and flow

6.5 Custom task types revisited

6.6 Summary

7 C# 5 bonus features

7.1 Capturing variables in foreach loops

7.2 Caller information attributes

7.2.1 Basic behavior

7.2.2 Logging

7.2.3 Simplifying INotifyPropertyChanged implementations

7.2.4 Corner cases of caller information attributes

7.2.5 Using caller information attributes with old versions of .NET

7.3 Summary

Part 3: C# 6

8 Super-sleek properties and expression-bodied members

8.1 A brief history of properties

8.2 Upgrades to automatically implemented properties

8.2.1 Read-only automatically implemented properties

8.2.2 Initializing automatically implemented properties

8.2.3 Automatically implemented properties in structs

8.3 Expression-bodied members

8.3.1 Even simpler read-only computed properties

8.3.2 Expression-bodied methods, indexers and operators

8.3.3 Restrictions on expression-bodied members in C# 6

8.3.4 Guidelines for using expression-bodied members

8.4 Summary

9 Stringy features

9.1 A recap on string formatting in .NET

9.1.1 Simple string formatting

9.1.2 Custom formatting with format strings

9.1.3 Localization

9.2 Introducing interpolated string literals

9.2.1 Simple interpolation

9.2.2 Format strings in interpolated string literals

9.2.3 Interpolated verbatim string literals

9.2.4 Compiler handling of interpolated string literals (part 1)

9.3 Localization using FormattableString

9.3.1 Compiler handling of interpolated string literals (part 2)

9.3.2 Formatting a FormattableString in a specific culture

9.3.3 Other uses for FormattableString

9.3.4 Using FormattableString with older versions of .NET

9.4 Uses, guidelines and limitations

9.4.1 Developers and machines, but maybe not end users

9.4.2 Hard limitations of interpolated string literals

9.4.3 When you can, but really shouldn’t

9.5 Accessing identifiers with nameof

9.5.1 Common uses of nameof

9.5.2 Tricks and traps when using nameof

9.6 Summary

10 A smörgåsbord of features for concise code

10.1 “Using static” directives

10.1.1 Importing static members

10.1.2 Extension methods and using static

10.2 Object and collection initializer enhancements

10.2.1 Indexers in object initializers

10.2.2 Using extension methods in collection initializers

10.2.3 Test code vs production code

10.3 The null conditional operator

10.3.1 Simple and safe property dereferencing

10.3.2 The null conditional operator in more detail

10.3.3 Handling Boolean comparisons

10.3.4 Indexers and the null conditional operator

10.3.5 Working effectively with the null conditional operator

10.3.6 Limitations of the null conditional operator

10.4 Exception filters

10.4.1 Syntax and semantics of exception filters

10.4.2 Retrying operations

10.4.3 Logging as a side-effect

10.4.4 Individual, case-specific exception filters

10.4.5 Why not just throw?

10.5 Summary

Part 4: C# 7 and beyond

11 Composition using tuples

11.1 Introduction to tuples

11.2 Tuple literals and tuple types

11.2.1 Syntax

11.2.2 Inferred element names for tuple literals (C# 7.1)

11.2.3 Tuples as bags of variables

11.3 Tuple types and conversions

11.3.1 Types of tuple literals

11.3.2 Conversions from tuple literals to tuple types

11.3.3 Conversions between tuple types

11.3.4 Uses of conversions

11.3.5 Element name checking in inheritance

11.3.6 Equality and inequality operators (C# 7.3)

11.4 Tuples in the CLR

11.4.1 Introducing System.ValueTuple<…​>

11.4.2 Element name handling

11.4.3 Tuple conversion implementations

11.4.4 String representations of tuples

11.4.5 Regular equality and ordering comparisons

11.4.6 Structural equality and ordering comparisons

11.4.7 Womples and large tuples

11.4.8 The non-generic ValueTuple struct

11.4.9 Extension methods

11.5 Alternatives to tuples

11.5.1 System.Tuple<…​>

11.5.2 Anonymous types

11.5.3 Named types

11.6 Uses and recommendations

11.6.1 Non-public APIs and easily-changed code

11.6.2 Local variables

11.6.3 Fields

11.6.4 Tuples and dynamic don’t play together nicely

11.7 Summary

12 Deconstruction and pattern matching

12.1 Deconstruction of tuples

12.1.1 Deconstruction to new variables

12.1.2 Deconstruction assignments to existing variables and properties

12.1.3 Details of tuple literal deconstruction

12.2 Deconstruction of non-tuple types

12.2.1 Instance deconstruction methods

12.2.2 Extension deconstruction methods and overloading

12.2.3 Compiler handling of Deconstruct calls

12.3 Introduction to pattern matching

12.4 Patterns available in C# 7.0

12.4.1 Constant patterns

12.4.2 Type patterns

12.4.3 The var pattern

12.5 Using patterns with the is operator

12.6 Using patterns with switch statements

12.6.1 Guard clauses

12.6.2 Pattern variable scope for case labels

12.6.3 Evaluation order of pattern-based switch statements

12.7 Thoughts on usage

12.7.1 Spotting deconstruction opportunities

12.7.2 Spotting pattern matching opportunities

12.8 Summary

13 Improving efficiency with more pass-by-reference

13.1 Recap: what do we know about ref?

13.2 Ref locals and ref returns

13.2.1 Ref locals

13.2.2 Ref return

13.2.3 The conditional ?: operator and ref values (C# 7.2)

13.2.4 Ref readonly (C# 7.2)

13.3 in parameters (C# 7.2)

13.3.1 Compatibility considerations

13.3.2 The surprising mutability of in parameters: external changes

13.3.3 Overloading with in parameters

13.3.4 Guidance for in parameters

13.4 Declaring structs as readonly (C# 7.2)

13.4.1 Background: implicit copying with read-only variables

13.4.2 The readonly modifier for structs

13.4.3 XML serialization is implicitly read-write

13.5 Extension methods with ref or in parameters (C# 7.2)

13.5.1 Using ref/in parameters in extension methods to avoid copying

13.5.2 Restrictions on ref and in extension methods

13.6 Ref-like structs (C# 7.2)

13.6.1 Rules for ref-like structs

13.6.2 Span<T> and stackalloc

13.6.3 IL representation of ref-like structs

13.7 Summary

14 Concise code

14.1 Local methods

14.1.1 Variable access within local methods

14.1.2 Local method implementations

14.1.3 Usage guidelines

14.2 Out variables

14.2.1 Inline variable declarations for out parameters

14.2.2 Restrictions lifted in 7.3 for out variables and pattern variables

14.3 Improvements to numeric literals

14.3.1 Binary integer literals

14.3.2 Underscore separators

14.4 Throw expressions

14.5 Default literals (C# 7.1)

14.5.1 Default literals and optional parameters

14.6 Non-trailing named arguments (C# 7.2)

14.7 Minor improvements in C# 7.3

14.7.1 Generic type constraints

14.7.2 Overload resolution improvements

14.7.3 Attributes for fields backing automatically implemented properties

14.8 Summary

15 C# 8 and beyond

15.1 Nullable reference types

15.1.1 What problem do nullable reference types solve?

15.1.2 Changing the meaning when using reference types

15.1.3 Enter nullable reference types

15.1.4 Nullable reference types at compile time and execution time

15.1.5 The “damn it” or “bang” operator

15.1.6 Experiences of nullable reference type migration

15.1.7 Future improvements

15.2 Switch expressions

15.3 Recursive pattern matching

15.3.1 Matching properties in patterns

15.3.2 Deconstruction patterns

15.3.3 Omitting types from patterns

15.4 Indexes and ranges

15.5 More async integration

15.5.1 Asynchronous resource disposal with “using await”

15.5.2 Asynchronous iteration with “foreach await”

15.5.3 Asynchronous iterators

15.6 Features not yet in preview

15.6.1 Default interface methods

15.6.2 Record types

15.6.3 Even more features in brief

15.7 Getting involved

15.8 Conclusion

Appendixes

Appendix A: Language features by version

A.1 C# 2

A.2 C# 3

A.3 C# 4

A.4 C# 5

A.5 C# 6

A.6 C# 7.0

A.7 C# 7.1

A.8 C# 7.2

A.9 C# 7.3

About the Technology

If you're a .NET developer, you'll use C# whether you're building an advanced enterprise application or just pushing out a quick ASP.NET app. C# 5, 6 and 7 have added a host of new features to help you write better code with tuples, string interpolation, pattern matching, and more. To really succeed with these powerful new features, however, you need to learn them in depth. This book is your ticket!

What's inside

  • The latest changes and updates for C# 5, 6, and 7
  • How C# works and why
  • Using asynchronous functions
  • Simpler string formatting with interpolation
  • Composition with tuples
  • Decomposition and pattern matching

About the reader

If you're a C# developer who's comfortable working with the language and wants to really dig in deep, then this book is for you.

About the author

Jon Skeet is a senior software engineer at Google. He studied mathematics and computer science at Cambridge, is a recognized authority in Java and C#, and maintains the position of top contributor to Stack Overflow.

Manning Early Access Program (MEAP) Read chapters as they are written, get the finished eBook as soon as it’s ready, and receive the pBook long before it's in bookstores.

FREE domestic shipping on three or more pBooks