C# in Depth, Fourth Edition

free previous edition eBook included An eBook copy of the previous edition of this book is included at no additional cost. It will be automatically added to your Manning Bookshelf within 24 hours of purchase.

Jon doesn’t just explain how C# works; he explains how the whole thing holds together as a unified design, and also points out when it doesn’t. From the Foreword by Eric Lippert, Facebook 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. Listen to this book in liveAudio! liveAudio integrates a professional voice recording with the book’s text, graphics, code, and exercises in Manning’s exclusive liveBook online reader. Use the text to search and navigate the audio, or download the audio-only recording for portable offline listening. You can purchase or upgrade to liveAudio here or in liveBook.

About the Technology The powerful, flexible C# programming language is the foundation of .NET development. Even after two decades of success, it's still getting better! Exciting new features in C# 6 and 7 make it easier than ever to take on big data applications, cloud-centric web development, and cross-platform software using .NET Core. There's never been a better time to learn C# in depth.

About the book C# in Depth, Fourth Edition is a revised edition of the bestseller written by C# legend Jon Skeet. This authoritative and engaging guide is your key to unlocking this powerful language, including the new features of C# 6 and 7. In it, Jon introduces expression-bodied members, interpolated strings, pattern matching, and more. Real-world examples drive it all home. By the end of this awesome book, you'll be writing C# code with skill, style, and confidence.

takes you straight to the book detailed table of contents Table of Contents Part 1: C# in context 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 Summary Part 2: C# 2-5 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 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 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 Summary 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 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 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 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 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 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? 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 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 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 Summary 14 Concise code in C# 7 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 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 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

What's inside Comprehensive coverage of C# 6 and 7

Greatest hits of C# 2–5

Extended pass-by-reference functionality

String interpolation

Composition with tuples

Decomposition and pattern matching

About the reader For intermediate C# developers.