| Template-Method-Pattern :👈 | 👉:Scalable API Architecture |
CRTP (Curiously Recurring Template Pattern) in C# |
🧩 CRTP (Curiously Recurring Template Pattern) in C#
The Curiously Recurring Template Pattern (CRTP) is a design technique where a class uses itself as a parameter to a generic base class. It’s more common in C++, but C# supports a similar approach using generics with constraints.
🔑 Core Idea
A base class defines behavior that depends on the derived class type.
This allows:
- Compile-time polymorphism (instead of runtime via virtual methods).
- Strongly typed self-references in generic hierarchies.
📖 Example in C#
using System;
// Base class using CRTP
public abstract class BaseViewModel<T> where T : BaseViewModel<T>
{
public void Initialize()
{
Console.WriteLine($"{typeof(T).Name} initializing...");
((T)this).OnInitialize(); // Call derived implementation
}
// Derived class must implement
protected abstract void OnInitialize();
}
// Derived ViewModel
public class UserViewModel : BaseViewModel<UserViewModel>
{
protected override void OnInitialize()
{
Console.WriteLine("UserViewModel: Loading user data...");
}
}
public class ProductViewModel : BaseViewModel<ProductViewModel>
{
protected override void OnInitialize()
{
Console.WriteLine("ProductViewModel: Loading product catalog...");
}
}
class Program
{
static void Main()
{
var userVM = new UserViewModel();
userVM.Initialize();
var productVM = new ProductViewModel();
productVM.Initialize();
}
}
📝 Output
UserViewModel initializing...
UserViewModel: Loading user data...
ProductViewModel initializing...
ProductViewModel: Loading product catalog...
✅ Pros
- Compile-time safety: Derived type is enforced by the generic constraint.
- Avoids virtual calls: Can reduce runtime overhead compared to polymorphism.
- Cleaner APIs: Base class can expose methods that return the derived type (fluent APIs).
- Consistency: Ensures derived classes follow a strict pattern.
⚠️ Cons
- Complexity: Can be confusing for developers unfamiliar with CRTP.
- Less flexible than runtime polymorphism: Works best when hierarchy is known at compile time.
- Limited in C# compared to C++: No template specialization, so patterns are less powerful.
- Harder debugging: Generic type recursion can make stack traces harder to read.
🏆 Best Practices
- Use CRTP when you want compile-time polymorphism and fluent APIs.
- Keep hierarchies shallow to avoid complexity.
- Document clearly why CRTP is used (many devs expect runtime polymorphism).
- Prefer CRTP for framework/lifecycle base classes (like ViewModels, Builders, Fluent APIs).
🚫 What to Avoid
- Don’t use CRTP just to “look clever” — it adds cognitive load.
- Avoid deep inheritance chains with CRTP (hard to maintain).
- Don’t mix CRTP with heavy runtime polymorphism — it defeats the purpose.
- Avoid exposing CRTP-based APIs to external consumers unless absolutely necessary (they may find it confusing).
⚡ Real-World Use Case
In MVVM frameworks (like .NET MAUI or WPF), CRTP can be used for a BaseViewModel lifecycle:
BaseViewModel<T>defines lifecycle hooks (Initialize,OnDestroy).- Each derived ViewModel (e.g.,
UserViewModel,ProductViewModel) enforces type safety and lifecycle consistency.
| Template-Method-Pattern :👈 | 👉:Scalable API Architecture |