Design .NET types for performance. Seal classes, use readonly structs, prefer static pure functions, avoid premature enumeration, and choose the right collection types.
Design .NET types for performance. Seal classes, use readonly structs, prefer static pure functions, avoid premature enumeration, and choose the right collection types.
invocable
false
Type Design for Performance
When to Use This Skill
Use this skill when:
Designing new types and APIs
Reviewing code for performance issues
Choosing between class, struct, and record
Working with collections and enumerables
Core Principles
Seal your types - Unless explicitly designed for inheritance
Prefer readonly structs - For small, immutable value types
Prefer static pure functions - Better performance and testability
Defer enumeration - Don't materialize until you need to
Return immutable collections - From API boundaries
Seal Classes by Default
Sealing classes enables JIT devirtualization and communicates API intent.
// DO: Seal classes not designed for inheritancepublicsealedclassOrderProcessor
{
publicvoidProcess(Order order) { }
}
;
{
{ }
}
// Slice without allocation
ReadOnlySpan<char> span = "Hello, World!".AsSpan();
var hello = span[..5]; // No allocation// Stack allocation for small buffers
Span<byte> buffer = stackallocbyte[256];
// Use ArrayPool for larger buffersvar buffer = ArrayPool<byte>.Shared.Rent(4096);
try
{
// Use buffer...
}
finally
{
ArrayPool<byte>.Shared.Return(buffer);
}
Collection Return Types
Return Immutable Collections from APIs
// DO: Return immutable collectionpublic IReadOnlyList<Order> GetOrders()
{
return _orders.ToList(); // Caller can't modify internal state
}
// DO: Use frozen collections for static data (.NET 8+)privatestaticreadonly FrozenDictionary<string, Handler> _handlers =
new Dictionary<string, Handler>
{
["create"] = new CreateHandler(),
["update"] = new UpdateHandler(),
}.ToFrozenDictionary();
// DON'T: Return mutable collectionpublic List<Order> GetOrders()
{
return _orders; // Caller can modify!
}
Internal Mutation is Fine
public IReadOnlyList<OrderItem> BuildOrderItems(Cart cart)
{
var items = new List<OrderItem>(); // Mutable internallyforeach (var cartItem in cart.Items)
{
items.Add(CreateOrderItem(cartItem));
}
return items; // Return as IReadOnlyList
}
Collection Guidelines
Scenario
Return Type
API boundary
IReadOnlyList<T>, IReadOnlyCollection<T>
Static lookup data
FrozenDictionary<K,V>, FrozenSet<T>
Internal building
List<T>, then return as readonly
Single item or none
T? (nullable)
Zero or more, lazy
IEnumerable<T>
Quick Reference
Pattern
Benefit
sealed class
Devirtualization, clear API
readonly record struct
No defensive copies, value semantics
Static pure functions
No vtable, testable, thread-safe
Defer .ToList()
Single materialization
ValueTask for hot paths
Avoid Task allocation
Span<T> for bytes
Stack allocation, no copying
IReadOnlyList<T> return
Immutable API contract
FrozenDictionary
Fastest lookup for static data
Anti-Patterns
// DON'T: Unsealed class without reasonpublicclassOrderService { } // Seal it!// DON'T: Mutable structpublicstruct Point { publicint X; publicint Y; } // Make readonly// DON'T: Instance method that could be staticpublicintAdd(int a, int b) => a + b; // Make static// DON'T: Multiple ToList() calls
items.Where(...).ToList().OrderBy(...).ToList(); // One ToList at end// DON'T: Return List<T> from public APIpublic List<Order> GetOrders(); // Return IReadOnlyList<T>// DON'T: ValueTask for always-async operationspublic ValueTask<Order> CreateOrderAsync(); // Just use Task