M A N N I N G IN DEPTH Jon Skeet FOREWORD BY ERIC LIPPERT FOURTH EDITION
Praise for the Third Edition “A must-have book that every .NET developer should read at least once.” —Dror Helper, Software Architect, Better Place “C# in Depth is the best source for learning C# language features.” —Andy Kirsch, Software Architect, Venga “Took my C# knowledge to the next level.” —Dustin Laine, Owner, Code Harvest “This book was quite an eye-opener to an interesting programming language that I have been unjustly ignoring until now.” —Ivan Todorović, Senior Software Developer AudatexGmbH, Switzerland “Easily the best C# reference I’ve found.” —Jon Parish, Software Engineer, Datasift “Highly recommend this book to C# developers who want to take their knowledge to pro status.” —D. Jay, Amazon reviewer Praise for the Second Edition “If you are looking to master C# then this book is a must-read.” —Tyson S. Maxwell, Sr. Software Engineer, Raytheon “We’re betting that this will be the best C# 4.0 book out there.” —Nikander Bruggeman and Margriet Bruggeman .NET consultants, Lois & Clark IT Services “A useful and engaging insight into the evolution of C# 4.” —Joe Albahari, Author of LINQPad and C# 4.0 in a Nutshell “This book should be required reading for all professional C# developers.” —Stuart Caborn, Senior Developer, BNP Paribas
ii“A highly focused, master-level resource on language updates across all major C# releases. This book is a must-have for the expert developer wanting to stay current with new features of the C# language.” —Sean Reilly, Programmer/Analyst Point2 Technologies “Why read the basics over and over again? Jon focuses on the chewy, new stuff!” —Keith Hill, Software Architect, Agilent Technologies “Everything you didn’t realize you needed to know about C#.” —Jared Parsons, Senior Software Development Engineer, Microsoft Praise for the First Edition “Simply put, C# in Depth is perhaps the best computer book I’ve read.” —Craig Pelkie, Author, System iNetwork “I have been developing in C# from the very beginning and this book had some nice surprises even for me. I was especially impressed with the excellent coverage of delegates, anonymous methods, covariance and contravariance. Even if you are a seasoned developer, C# in Depth will teach you something new about the C# language.... This book truly has depth that no other C# language book can touch.” —Adam J. Wolf, Southeast Valley .NET User Group “This book wraps up the author’s great knowledge of the inner workings of C# and hands it over to readers in a well-written, concise, usable book.” —Jim Holmes, Author of Windows Developer Power Tools “Every term is used appropriately and in the right context, every example is spot-on and con- tains the least amount of code that shows the full extent of the feature...this is a rare treat.” —Franck Jeannin, Amazon UK reviewer “If you have developed using C# for several years now, and would like to know the internals, this book is absolutely right for you.” —Golo Roden Author, Speaker, and Trainer for .NET and related technologies “The best C# book I’ve ever read.” —Chris Mullins, C# MVP
C# in Depth FOURTH EDITION JON SKEET FOREWORD BY ERIC LIPPERT M A N N I N G SHELTER ISLAND
For online information and ordering of this and other Manning books, please visit www.manning.com. The publisher offers discounts on this book when ordered in quantity. For more information, please contact Special Sales Department Manning Publications Co. 20 Baldwin Road PO Box 761 Shelter Island, NY 11964 Email: orders@manning.com ©2019 by Manning Publications Co. All rights reserved. No part of this publication may be reproduced, stored in a retrieval system, or transmitted, in any form or by means electronic, mechanical, photocopying, or otherwise, without prior written permission of the publisher. Many of the designations used by manufacturers and sellers to distinguish their products are claimed as trademarks. Where those designations appear in the book, and Manning Publications was aware of a trademark claim, the designations have been printed in initial caps or all caps. Recognizing the importance of preserving what has been written, it is Manning’s policy to have the books we publish printed on acid-free paper, and we exert our best efforts to that end. Recognizing also our responsibility to conserve the resources of our planet, Manning books are printed on paper that is at least 15 percent recycled and processed without the use of elemental chlorine. Manning Publications Co. Development editor: Richard Wattenberger 20 Baldwin Road Technical development editor: Dennis Sellinger PO Box 761 Review editor: Ivan Martinović Shelter Island, NY 11964 Production editor: Lori Weidert Copy editor: Sharon Wilkey Technical proofreader: Eric Lippert Typesetter and cover designer: Marija Tudor ISBN 9781617294532 Printed in the United States of America 1 2 3 4 5 6 7 8 9 10 – SP – 24 23 22 21 20 19
This book is dedicated to equality, which is significantly harder to achieve in the real world than overriding Equals() and GetHashCode().
contents foreword xvii preface xix acknowledgments xx about this book xxii about the author xxvi about the cover illustration xxvii PART 1 C# IN CONTEXT ............................................... 1 1 Survival of the sharpest 3 1.1 An evolving language 3 A helpful type system at large and small scales 4 ■ Ever more concise code 6 ■ Simple data access with LINQ 9 Asynchrony 10 ■ Balancing efficiency and complexity 11 Evolution at speed: Using minor versions 12 1.2 An evolving platform 13 1.3 An evolving community 14 1.4 An evolving book 15 Mixed-level coverage 16 ■ Examples using Noda Time 16 Terminology choices 17vii
CONTENTSviiiPART 2 C# 2–5 .......................................................... 19 2 C# 2 21 2.1 Generics 22 Introduction by example: Collections before generics 22 Generics save the day 25 ■ What can be generic? 29 Type inference for type arguments to methods 30 ■ Type constraints 32 ■ The default and typeof operators 34 Generic type initialization and state 37 2.2 Nullable value types 38 Aim: Expressing an absence of information 39 ■ CLR and framework support: The Nullable<T> struct 40 ■ Language support 43 2.3 Simplified delegate creation 49 Method group conversions 50 ■ Anonymous methods 50 Delegate compatibility 52 2.4 Iterators 53 Introduction to iterators 54 ■ Lazy execution 55 ■ Evaluation of yield statements 56 ■ The importance of being lazy 57 Evaluation of finally blocks 58 ■ The importance of finally handling 61 ■ Implementation sketch 62 2.5 Minor features 66 Partial types 67 ■ Static classes 69 ■ Separate getter/setter access for properties 69 ■ Namespace aliases 70 Pragma directives 72 ■ Fixed-size buffers 73 InternalsVisibleTo 73 3 C# 3: LINQ and everything that comes with it 75 3.1 Automatically implemented properties 76 3.2 Implicit typing 77 Typing terminology 77 ■ Implicitly typed local variables (var) 78 ■ Implicitly typed arrays 79 3.3 Object and collection initializers 81 Introduction to object and collection initializers 81 Object initializers 83 ■ Collection initializers 84 The benefits of single expressions for initialization 86 3.4 Anonymous types 86 Syntax and basic behavior 86 ■ The compiler-generated type 89 ■ Limitations 90
CONTENTS ix3.5 Lambda expressions 91 Lambda expression syntax 92 ■ Capturing variables 94 Expression trees 101 3.6 Extension methods 103 Declaring an extension method 103 ■ Invoking an extension method 104 ■ Chaining method calls 106 3.7 Query expressions 107 Query expressions translate from C# to C# 108 ■ Range variables and transparent identifiers 108 ■ Deciding when to use which syntax for LINQ 110 3.8 The end result: LINQ 111 4 C# 4: Improving interoperability 113 4.1 Dynamic typing 114 Introduction to dynamic typing 114 ■ Dynamic behavior beyond reflection 119 ■ A brief look behind the scenes 124 Limitations and surprises in dynamic typing 127 ■ Usage suggestions 131 4.2 Optional parameters and named arguments 133 Parameters with default values and arguments with names 134 Determining the meaning of a method call 135 ■ Impact on versioning 137 4.3 COM interoperability improvements 138 Linking primary interop assemblies 139 ■ Optional parameters in COM 140 ■ Named indexers 142 4.4 Generic variance 143 Simple examples of variance in action 143 ■ Syntax for variance in interface and delegate declarations 144 Restrictions on using variance 145 ■ Generic variance in practice 147 5 Writing asynchronous code 150 5.1 Introducing asynchronous functions 152 First encounters of the asynchronous kind 152 ■ Breaking down the first example 154 5.2 Thinking about asynchrony 155 Fundamentals of asynchronous execution 155 ■ Synchronization contexts 157 ■ Modeling asynchronous methods 158
CONTENTSx5.3 Async method declarations 160 Return types from async methods 161 ■ Parameters in async methods 162 5.4 Await expressions 162 The awaitable pattern 163 ■ Restrictions on await expressions 165 5.5 Wrapping of return values 166 5.6 Asynchronous method flow 168 What is awaited and when? 168 ■ Evaluation of await expressions 169 ■ The use of awaitable pattern members 173 Exception unwrapping 174 ■ Method completion 176 5.7 Asynchronous anonymous functions 180 5.8 Custom task types in C# 7 182 The 99.9% case: ValueTask<TResult> 182 ■ The 0.1% case: Building your own custom task type 184 5.9 Async main methods in C# 7.1 186 5.10 Usage tips 187 Avoid context capture by using ConfigureAwait (where appropriate) 187 ■ Enable parallelism by starting multiple independent tasks 189 ■ Avoid mixing synchronous and asynchronous code 190 ■ Allow cancellation wherever possible 190 ■ Testing asynchrony 191 6 Async implementation 193 6.1 Structure of the generated code 195 The stub method: Preparation and taking the first step 198 Structure of the state machine 199 ■ The MoveNext() method (high level) 202 ■ The SetStateMachine method and the state machine boxing dance 204 6.2 A simple MoveNext() implementation 205 A full concrete example 205 ■ MoveNext() method general structure 207 ■ Zooming into an await expression 209 6.3 How control flow affects MoveNext() 210 Control flow between await expressions is simple 211 Awaiting within a loop 212 ■ Awaiting within a try/finally block 213 6.4 Execution contexts and flow 216 6.5 Custom task types revisited 218
CONTENTS xi7 C# 5 bonus features 220 7.1 Capturing variables in foreach loops 220 7.2 Caller information attributes 222 Basic behavior 222 ■ Logging 224 ■ Simplifying INotifyPropertyChanged implementations 224 ■ Corner cases of caller information attributes 226 ■ Using caller information attributes with old versions of .NET 232 PART 3 C# 6 ............................................................ 233 8 Super-sleek properties and expression-bodied members 235 8.1 A brief history of properties 236 8.2 Upgrades to automatically implemented properties 238 Read-only automatically implemented properties 238 Initializing automatically implemented properties 239 Automatically implemented properties in structs 240 8.3 Expression-bodied members 242 Even simpler read-only computed properties 242 ■ Expression- bodied methods, indexers, and operators 245 ■ Restrictions on expression-bodied members in C# 6 247 ■ Guidelines for using expression-bodied members 249 9 Stringy features 252 9.1 A recap on string formatting in .NET 253 Simple string formatting 253 ■ Custom formatting with format strings 253 ■ Localization 255 9.2 Introducing interpolated string literals 258 Simple interpolation 258 ■ Format strings in interpolated string literals 259 ■ Interpolated verbatim string literals 259 Compiler handling of interpolated string literals (part 1) 261 9.3 Localization using FormattableString 261 Compiler handling of interpolated string literals (part 2) 262 Formatting a FormattableString in a specific culture 263 Other uses for FormattableString 265 ■ Using FormattableString with older versions of .NET 268 9.4 Uses, guidelines, and limitations 270 Developers and machines, but maybe not end users 270 Hard limitations of interpolated string literals 272 ■ When you can but really shouldn’t 273
CONTENTSxii9.5 Accessing identifiers with nameof 275 First examples of nameof 275 ■ Common uses of nameof 277 Tricks and traps when using nameof 280 10 A smörgåsbord of features for concise code 284 10.1 Using static directives 284 Importing static members 285 ■ Extension methods and using static 288 10.2 Object and collection initializer enhancements 290 Indexers in object initializers 291 ■ Using extension methods in collection initializers 294 ■ Test code vs. production code 298 10.3 The null conditional operator 299 Simple and safe property dereferencing 299 ■ The null conditional operator in more detail 300 ■ Handling Boolean comparisons 301 ■ Indexers and the null conditional operator 302 ■ Working effectively with the null conditional operator 303 ■ Limitations of the null conditional operator 305 10.4 Exception filters 305 Syntax and semantics of exception filters 306 ■ Retrying operations 311 ■ Logging as a side effect 312 ■ Individual, case-specific exception filters 313 ■ Why not just throw? 314 PART 4 C# 7 AND BEYOND ....................................... 317 11 Composition using tuples 319 11.1 Introduction to tuples 320 11.2 Tuple literals and tuple types 321 Syntax 321 ■ Inferred element names for tuple literals (C# 7.1) 323 ■ Tuples as bags of variables 324 11.3 Tuple types and conversions 329 Types of tuple literals 329 ■ Conversions from tuple literals to tuple types 330 ■ Conversions between tuple types 334 ■ Uses of conversions 336 ■ Element name checking in inheritance 336 Equality and inequality operators (C# 7.3) 337 11.4 Tuples in the CLR 338 Introducing System.ValueTuple<...> 338 ■ Element name handling 339 ■ Tuple conversion implementations 341 String representations of tuples 341 ■ Regular equality and ordering comparisons 342 ■ Structural equality and ordering
CONTENTS xiiicomparisons 343 ■ Womples and large tuples 345 ■ The nongeneric ValueTuple struct 346 ■ Extension methods 346 11.5 Alternatives to tuples 346 System.Tuple<...> 347 ■ Anonymous types 347 Named types 348 11.6 Uses and recommendations 348 Nonpublic APIs and easily changed code 348 ■ Local variables 349 ■ Fields 350 ■ Tuples and dynamic don’t play together nicely 351 12 Deconstruction and pattern matching 353 12.1 Deconstruction of tuples 354 Deconstruction to new variables 355 ■ Deconstruction assignments to existing variables and properties 357 Details of tuple literal deconstruction 361 12.2 Deconstruction of nontuple types 361 Instance deconstruction methods 362 ■ Extension deconstruction methods and overloading 363 ■ Compiler handling of Deconstruct calls 364 12.3 Introduction to pattern matching 365 12.4 Patterns available in C# 7.0 367 Constant patterns 367 ■ Type patterns 368 ■ The var pattern 371 12.5 Using patterns with the is operator 372 12.6 Using patterns with switch statements 374 Guard clauses 375 ■ Pattern variable scope for case labels 376 ■ Evaluation order of pattern-based switch statements 377 12.7 Thoughts on usage 379 Spotting deconstruction opportunities 379 ■ Spotting pattern matching opportunities 380 13 Improving efficiency with more pass by reference 381 13.1 Recap: What do you know about ref? 382 13.2 Ref locals and ref returns 385 Ref locals 385 ■ Ref returns 390 ■ The conditional ?: operator and ref values (C# 7.2) 392 ■ Ref readonly (C# 7.2) 393 13.3 in parameters (C# 7.2) 395
CONTENTSxivCompatibility considerations 396 ■ The surprising mutability of in parameters: External changes 397 ■ Overloading with in parameters 398 ■ Guidance for in parameters 399 13.4 Declaring structs as readonly (C# 7.2) 401 Background: Implicit copying with read-only variables 401 The readonly modifier for structs 403 ■ XML serialization is implicitly read-write 404 13.5 Extension methods with ref or in parameters (C# 7.2) 405 Using ref/in parameters in extension methods to avoid copying 405 Restrictions on ref and in extension methods 407 13.6 Ref-like structs (C# 7.2) 408 Rules for ref-like structs 409 ■ Span<T> and stackalloc 410 IL representation of ref-like structs 414 14 Concise code in C# 7 415 14.1 Local methods 415 Variable access within local methods 417 ■ Local method implementations 420 ■ Usage guidelines 425 14.2 Out variables 427 Inline variable declarations for out parameters 427 ■ Restrictions lifted in C# 7.3 for out variables and pattern variables 428 14.3 Improvements to numeric literals 429 Binary integer literals 429 ■ Underscore separators 430 14.4 Throw expressions 431 14.5 Default literals (C# 7.1) 432 14.6 Nontrailing named arguments (C# 7.2) 433 14.7 Private protected access (C# 7.2) 435 14.8 Minor improvements in C# 7.3 435 Generic type constraints 435 ■ Overload resolution improvements 436 ■ Attributes for fields backing automatically implemented properties 437 15 C# 8 and beyond 439 15.1 Nullable reference types 440 What problem do nullable reference types solve? 440 ■ Changing the meaning when using reference types 441 ■ Enter nullable reference types 442 ■ Nullable reference types at compile time and
CONTENTS xvexecution time 443 ■ The damn it or bang operator 445 Experiences of nullable reference type migration 447 Future improvements 449 15.2 Switch expressions 453 15.3 Recursive pattern matching 455 Matching properties in patterns 455 ■ Deconstruction patterns 456 ■ Omitting types from patterns 457 15.4 Indexes and ranges 458 Index and Range types and literals 458 ■ Applying indexes and ranges 459 15.5 More async integration 461 Asynchronous resource disposal with using await 461 Asynchronous iteration with foreach await 462 ■ Asynchronous iterators 465 15.6 Features not yet in preview 466 Default interface methods 466 ■ Record types 468 Even more features in brief 469 15.7 Getting involved 470 appendix Language features by version 473 index 479
foreword Ten years is a long stretch of time for a human, and it’s an absolute eternity for a tech- nical book aimed at professional programmers. It was with some astonishment, then, that I realized 10 years have passed since Microsoft shipped C# 3.0 with Visual Studio 2008 and since I read the drafts of the first edition of this book. It has also been 10 years since Jon joined Stack Overflow and quickly became the user with the highest reputation. C# was already a large, complex language in 2008, and the design and implemen- tation teams haven’t been idle for the last decade. I’m thrilled with how C# has been innovative in meeting the needs of many different developer constituencies, from video games to websites to low-level, highly robust system components. C# takes the best from academic research and marries it to practical techniques for solving real problems. It’s not dogmatic; the C# designers don’t ask “What’s the most object- oriented way to design this feature?” or “What’s the most functional way to design this feature?” but rather “What’s the most pragmatic, safe, and effective way to design this feature?” Jon gets all of that. He doesn’t just explain how the language works; he explains how the whole thing holds together as a unified design and also points out when it doesn’t. I said in my foreword to the first edition that Jon is enthusiastic, knowledgeable, talented, curious, analytical, and a great teacher, and all of that is still true. Let me add to that list by noting his perseverance and dedication. Writing a book is a huge job, particularly when you do it in your spare time. Going back and revising that book to keep it fresh and current is just as much work, and this is the third time Jon has done that with this book. A lesser author would be content to tweak it here and there or addxvii
FOREWORDxviiia chapter about new materials; this is more like a large-scale refactoring. The results speak for themselves. More than ever, I can’t wait to find out what great things the next generation of programmers will do with C# as it continues to evolve and grow. I hope you enjoy this book as much as I have over the years, and thanks for choosing to compose your pro- grams in C#. ERIC LIPPERT SOFTWARE ENGINEER FACEBOOK
preface Welcome to the fourth edition of C# in Depth. When I wrote the first edition, I had lit- tle idea I’d be writing a fourth edition of the same title 10 years later. Now, it wouldn’t surprise me to find myself writing another edition in 10 years. Since the first edition, the designers of the C# language have repeatedly proved that they’re dedicated to evolving the language for as long as the industry is interested in it. This is important, because the industry has changed a lot in the last 10 years. As a reminder, both the mobile ecosystem (as we know it today) and cloud computing were still in their infancy in 2008. Amazon EC2 was launched in 2006, and Google AppEngine was launched in 2008. Xamarin was launched by the Mono team in 2011. Docker didn’t show up until 2013. For many .NET developers, the really big change in our part of the computing world over the last few years has been .NET Core. It’s a cross-platform, open source version of the framework that is explicitly designed for compatibility with other frame- works (via .NET Standard). Its existence is enough to raise eyebrows; that it is Micro- soft’s primary area of investment in .NET is even more surprising. Through all of this, C# is still the primary language when targeting anything like .NET, whether that’s .NET, .NET Core, Xamarin, or Unity. F# is a healthy and friendly competitor, but it doesn’t have the industry mindshare of C#. I’ve personally been developing in C# since around 2002, either professionally or as an enthusiastic amateur. As the years have gone by, I’ve been sucked ever deeper into the details of the language. I enjoy those details for their own sake but, more importantly, for the sake of ever-increasing productivity when writing code in C#. I hope that some of that enjoyment has seeped into this book and will encourage you further in your travels with C#. xix
Comments 0
Loading comments...
Reply to Comment
Edit Comment