Harness the power of the C# type system to write programs that are clearer, simpler, and more efficient.
The type system is the foundation upon which all C# programs are built. The C# Type System will show you how todefine and implement value types effectively, and write more performant and robust code. Real-world code examplesand test cases throughout will elevate your programming with C# and show you how best to implement the principlesyou’re learning.
According to the C# Language Specification, “C# is intended to be a simple, modern, general-purpose, object-oriented programming language.” C# may look simple on the surface, but it has hidden depths even in its most fundamental features. The type system is a central element of C# and is the foundation upon which all C# programs are built. This has been true from the earliest versions of C#, and will continue to be relevant as the language evolves. The C# Type System isn’t a gallery of the latest language features, and we won’t delve into every detail of the latest edition, because C# is constantly advancing; instead we’ll focus on the language’s rich support for creating your own types and examine how best to employ the type system to improve your designs and write clearer, simpler, more efficient programs.
Many languages allow users to define their own types, but C# is different in that it makes a clear distinction between classes, which are reference types, and value types. Classes are the default choice for implementing a design in C# and the general-purpose mechanism for custom types, as they support all the object-oriented features that C# has to offer. In contrast, value types are much more specialized, which is why they’re often misunderstood and dismissed as an advanced feature that’s irrelevant for most applications. It’s true that value types aren’t suitable for many custom types in an application, and they may not be necessary in every design, but they have several advantages that are frequently underappreciated.
Among the core aspects of working with the type system, you’ll learn
• How user-defined value types, and even simple types, can enhance your code’s readability
• How reference and value types differ within C#
• How method parameters and arguments relate to reference and value types
• How differences in copy semantics between value and reference types affect a program’s behavior
• How the different methods of value comparisons for equality work behind the scenes
• The unique characteristics and roles of various types in an application, especially how value types go beyond meredata storage
• Why inheritance isn’t optimal for value types
• How to measure and evaluate the performance of an app’s use of different data types
Whether you’re a novice or seasoned programmer, you’ll find The C# Type System indispensable in your efforts to turngood code into great.
Author(s): Steve Love
Publisher: No Starch Press
Year: 2023
Language: English
Pages: 312
Cover
Title Page
Copyright
Dedication
About the Author and Technical Reviewer
Acknowledgments
Introduction
Who Should Read This Book
Organization and Requirements
Modern Features
Why Value Types?
1. Making the Most of the Type System
The Value of Good Names
Adding Clarity Through Types
Named Arguments
Custom Types
Encapsulation
Immutability
Value Validation
Testing
Refactoring
Replacing Magic Numbers with Named Constants
Simplifying Properties and Values
Overloading Arithmetic Operators
Determining a Need for New Types
Encoding Units
Itemizing Units with enums
Static Creation Methods
Symmetry in Design
Making Units Explicit
Choosing the Most Natural Usage
Returning Types Implied by Units
A Fully Formed Encapsulated Value
Deciding Whether to Abstract Types
Summary
2. Value and Reference Types
User-Defined Types
Structs and Classes
Records and Record Structs
Inheritance
Type Instance Lifetimes
Variables
Variables vs. Values
Definite Assignment
Instances and Storage
Embedded Values
Boxed Values
Semantics and Type
The Common Type System
Copy Semantics
Records, Structs, and Value Semantics
Construction and Initialization
Default Initialization
Instance Constructors
Field Initializers
Object Initializers
null Values and Default Values
Generics and null
Generics and Default Values
Nullable Value Types
Nullable Reference Types
The Null-Forgiving Operator
Summary
3. Reference and Value Parameters
Method Parameters and Arguments
Reference Types vs. By-Reference Parameters
Value Types and Parameters
The Value of a Reference
Reference Variables and Aliasing
Mutable By-Reference Parameters
Passing References by Reference
Passing Values by Reference
Working with Output Parameters
Limitations of By-Reference Parameters
Property Values
Overloading on By-Reference Parameters
Using Fields
Extension Methods
Side Effects and Direct Effects
Mutation vs. Creation
Declarative Code and Performance
Read-Only References and Returning by Reference
Returning Values by Reference
Preventing Modifications to Data
Keeping By-Reference Variables Within Scope
Considering Performance vs. Simplicity
Final Word on Mutable By-Reference Parameters
Summary
4. Implicit and Explicit Copying
Copying by Simple Assignment
Value Copy Behavior
Read-Only Properties vs. Immutable Types
Creating New Objects
Overwriting a Value
Constructing Value Types
Copying Records Like Value Types
Identifying Unnecessary Boxing
To an Interface
In Method Calls
Method Parameters and Arguments
Passing and Returning by Value
Accessing Properties
Using Expressions with Operators
Modifying Return Type Instances
Reference Type Properties
Instance Methods and Mutability
Properties as Arguments for Read-Only Parameters
Defensive Copies
Mutable Value Types and in Parameters
Automatic vs. Nonautomatic Properties
Read-Only Reference Variables
Read-Only Fields
Defending Against Mutation
Read-Only Accessors and Methods
Read-Only Types
Summary
5. Types of Equality
Built-in Equality
Whole Numbers
Floating-Point Values
Reference Equality
Strings and Value Equality
Custom Equality for Classes
Defining Equality Operators
Handling Comparisons with null
Making Type-Safe Comparisons
Working with Hash Codes
Structs and Equality
Overriding Equals for Structs
Boxing Values and Comparing by Identity
Comparing Generic Variables
Generic Code and the Equals Method
The IEquatable Interface
Compiler-Generated Equality
Records and Record Structs
Equality for Nullable Values
Value Tuples and Equality
Summary
6. The Nature of Values
Value vs. Reference Semantics
Copying and Equality Comparison Behavior
Mutability
Mechanics vs. Semantics
Object Relationships
Kinds of Objects
Object Characteristics
Design Refinement to Model Object Roles
Abstraction and Vocabulary
Encapsulation and Cohesion
Eliminating Duplication
Establishing Class Invariants
Clarifying with Symmetry
Encapsulation and the Public Interface
Extending the Interface
Reducing the Internal Interface
Composing Abstractions
Choosing Between Value and Reference Semantics
Avoiding the Pitfalls of Default Variables
Implementing Custom vs. Generated Behavior
Overriding Generated Methods
Comparison for Ordering
Equivalence vs. Equality
The Contract for Comparisons
Other Kinds of Ordering
The Perils of Uniformity and Consistency
Arithmetic and Nonarithmetic Types
Nonstandard Operator Behavior
Summary
7. Value Types and Polymorphism
Why Value Types Are Sealed
Implementation Inheritance
Value-Based Equality for Classes
Equality Behavior in Derived Classes
Equality Comparisons and Type Substitution
Inclusion Polymorphism and Subtyping
Working with Input and Output Types of Virtual Methods
Upholding a Type’s Contract
Inheriting Record Types
Avoiding Implementation Inheritance
Containing Instead of Inheriting Types
Parametric Polymorphism with Generics
Generic Constraints and Protocol Interfaces
Generic Method Parameters and Type Deduction
Parameterized Types
Ad Hoc Polymorphism with Overloading
Symbolic Polymorphism with Overloaded Operators
Generic Delegates for Polymorphism
Coercion Polymorphism Using Conversions
Widening vs. Narrowing Conversions
For Representation
For Purpose
Summary
8. Performance and Efficiency
Measuring and Optimizing Performance
The JIT Compiler
Performance Benchmarks
The Profiler
Measuring Basic Performance with Equals
Hidden Costs of Simplicity
The ValueType.Equals Method
The ValueType.GetHashCode Method
The HashCode.Combine Method
Optimizing Equality
The Effect of IEquatable
Property Accesses
The Equality Operators
How Type Affects Performance
Measuring the Cost of Copying
Copying Large Instances
Weighing Object Construction Costs
Measuring the Compiler-Generated Equals Method
How Common Idioms and Practices Affect Performance
Looping and Iteration
Pattern Matching and Selection
Summary
Afterword
Appendix: Further Reading
Index