Value Types
Value types (struct, enum, primitives) are copied on assignment and typically stored on the stack. They provide predictable performance and value semantics.Value Type Characteristics
- Value types derive from
System.ValueType - Assignment creates a full copy, preventing aliasing bugs
- readonly struct - Prevents all mutation after construction
- record struct (C# 10) - Provides value equality and non-destructive mutation
- in parameters - Pass structs by reference without copying
- Enums are backed by
intby default; specify underlying type explicitly
Advanced Struct Features
Span<T>is a ref struct, stack-only, enabling zero-copy slicing- Default struct is mutable — use
readonly structfor immutability - Record structs provide value equality, ToString, and Deconstruct
- Avoid large mutable structs (>16 bytes) — may be costlier to copy than reference types
Reference Types
Classes, interfaces, delegates, and strings are reference types. Variables hold references to heap-allocated objects, enabling shared state and polymorphism.Reference Type Features
nullis the default value for reference type variables- Nullable reference types -
#nullable enableadds compile-time null analysis - Record classes - Provide non-destructive mutation via
withexpressions - String is immutable; use
StringBuilderfor high-frequency concatenation WeakReference<T>prevents GC rooting of cached objects
Prefer record over class for DTOs and domain value objects — you get structural equality, ToString, and with-expressions for free.
Generics
Generics enable type-safe, reusable algorithms and data structures without boxing overhead.Generic Constraints
- Common Constraints
- Variance
where T : new()- Requires parameterless constructorwhere T : struct- Constrains to value typeswhere T : class?- Constrains to nullable reference typeswhere T : IComparable<T>- Interface constraint
Generic Best Practices
- Generic types and methods are compiled with type parameters resolved at JIT time
- Specialized code per value type; shared code per reference type
List<T>outperformsArrayListbecause no boxing for value types- Generic methods prefer type inference — explicit type args add clutter
Nullable Types
Nullable<T> (T?) wraps value types to represent an absent value. Nullable reference type annotations add compile-time null-safety for reference types.
Nullable Operators
- ?. - Null-conditional operator, short-circuits to null
- ?? - Null-coalescing operator, provides fallback value
- ??= - Null-coalescing assignment, assigns only when null
Nullable<T>.ValuethrowsInvalidOperationExceptionifHasValueis false- ! - Null-forgiving operator suppresses warnings (use sparingly)
Enumerations
Enums provide named, type-safe integral constants. They improve code readability and prevent invalid constant values.Enum Guidelines
- Always specify explicit integer values for persisted/serialized enums
[Flags]attribute enables bitwise combination of enum membersEnum.IsDefinedguards against invalid cast conversions- Use switch expressions for exhaustive enum pattern matching
- Enum underlying types:
byte,short,int(default),long
List<T>
The most commonly used collection type in .NET. Provides dynamic array functionality with efficient random access.List Features
- Dynamic resizing - Grows automatically as needed
- Efficient random access - O(1) by index
- LINQ support - All standard query operators
Capacity- Pre-allocate to avoid resizingTrimExcess()- Reduces memory footprint
Dictionary<TKey, TValue>
Hash table providing fast key-based lookups.Dictionary Best Practices
- Use
TryGetValueinstead of checkingContainsKeythen indexing - Implement
IEquatable<T>and overrideGetHashCodefor custom keys ConcurrentDictionary<K,V>for thread-safe scenariosIReadOnlyDictionary<K,V>for read-only APIs
HashSet<T>
Unordered collection of unique elements with O(1) membership testing.Set Operations
UnionWith- Adds all elements from another collectionIntersectWith- Keeps only common elementsExceptWith- Removes elements found in other collectionSymmetricExceptWith- XOR operation
Interfaces
Interfaces define a set of members that implementing types must provide. Essential for dependency injection, testability, and cross-cutting concerns.Interface Features
- Default interface members (C# 8+) - Enable non-breaking API evolution
- Explicit implementation - Resolves member name conflicts
- Generic interfaces - Enable covariant and contravariant usage
IDisposableandIAsyncDisposable- Manage resource lifetimes- Marker interfaces (empty) are superseded by attributes in most cases
Polymorphism
Polymorphism allows derived classes to override base class behavior, enabling open/closed extension without modifying existing code.Polymorphism Concepts
virtual+override= runtime polymorphism via virtual dispatchabstractmembers must be overridden in non-abstract derived classessealedprevents further derivation or method overridenewkeyword hides, not overrides — usually unintentional- Covariant return types (C# 9) allow overrides to return derived types
Seal classes by default in domain models to prevent unintended extension — unseal explicitly when inheritance is a designed use case.
Abstract Base Classes
Share behavior among related types with abstract classes
Seal Concrete Classes
Seal concrete leaf classes to convey intent and enable optimizations