Structs are value types that can contain multiple fields:
public struct Point{ public int X; public int Y; public Point(int x, int y) { X = x; Y = y; } public double DistanceFromOrigin() => Math.Sqrt(X * X + Y * Y);}// UsagePoint p = new Point(3, 4);Console.WriteLine(p.DistanceFromOrigin()); // 5.0// Structs are copied by valuePoint p2 = p; // Creates a copyp2.X = 10; // Does not affect p
Use structs for small, immutable data structures. Large structs have copy overhead - prefer classes for large data structures.
// Inefficient - creates many intermediate stringsstring result = "";for (int i = 0; i < 1000; i++){ result += i.ToString(); // DON'T DO THIS}// Efficient - uses mutable buffervar builder = new StringBuilder();for (int i = 0; i < 1000; i++){ builder.Append(i);}string result = builder.ToString();
String Memory Optimization: Use Span<char> and ReadOnlySpan<char> for zero-allocation string processing:
string text = "Hello,World";ReadOnlySpan<char> span = text.AsSpan();int comma = span.IndexOf(',');ReadOnlySpan<char> before = span[..comma]; // "Hello" - no allocationReadOnlySpan<char> after = span[(comma + 1)..]; // "World" - no allocation
public class Customer{ // Fields private string _name; // Properties public string Name { get => _name; set => _name = value ?? throw new ArgumentNullException(nameof(value)); } public int Age { get; set; } // Auto-property with initializer public DateTime CreatedAt { get; init; } = DateTime.UtcNow; // Constructor public Customer(string name, int age) { Name = name; Age = age; } // Method public string GetGreeting() => $"Hello, {Name}!";}// Usagevar customer = new Customer("Alice", 30);Console.WriteLine(customer.GetGreeting());
Understanding memory allocation is crucial for performance:
public void Example(){ // Stack allocation int x = 42; // Value type - on stack Point p = new Point(3, 4); // Struct - on stack // Heap allocation var customer = new Customer("Alice", 30); // Class - on heap var numbers = new int[100]; // Array - on heap // Boxing: value type → heap object boxed = x; // Heap allocation - avoid in hot paths! // Stack allocation with stackalloc Span<int> buffer = stackalloc int[128]; // Stack - very fast buffer[0] = 42;}
Boxing and Unboxing: Implicit conversion of value types to object causes boxing (heap allocation). Avoid boxing in performance-critical code:
int? nullableInt = null;double? nullableDouble = 3.14;// Checking for valueif (nullableInt.HasValue){ int value = nullableInt.Value;}// Null-coalescingint result = nullableInt ?? 0; // Use 0 if null// Null-conditionalint? doubled = nullableInt?.ToString().Length;
double d = 123.45;int i = (int)d; // Explicit cast: 123 (truncates decimal)long big = 1000000000000L;int small = (int)big; // May overflow if too large
object obj = "Hello";// Type testing with 'is'if (obj is string){ string s = (string)obj;}// Pattern matching (C# 7.0+)if (obj is string s){ Console.WriteLine(s.ToUpper());}// Safe cast with 'as'string? text = obj as string; // null if not stringif (text != null){ Console.WriteLine(text);}
// Good - small immutable structpublic readonly struct Point{ public int X { get; init; } public int Y { get; init; }}// Bad - large mutable struct has copy overheadpublic struct HugeData // Use class instead{ public byte[] Buffer; // Large array}