|
30 | 30 | - Lazy load components when possible |
31 | 31 | - Use `ShouldRender()` to optimize rendering |
32 | 32 |
|
| 33 | +## Memory Management & Leak Prevention |
| 34 | +### IDisposable/IAsyncDisposable Patterns |
| 35 | +- Always dispose `SemaphoreSlim` in try-finally blocks within DisposeAsync |
| 36 | +- Use disposal guard flags (`_disposed`) to prevent multiple disposals |
| 37 | +- Implement `IAsyncDisposable` for components using `CancellationTokenSource` |
| 38 | +- Dispose `CancellationTokenSource` in components (especially for animations/timers) |
| 39 | +- Call `Cancel()` before `Dispose()` on CancellationTokenSource |
| 40 | + |
| 41 | +### JavaScript Interop Cleanup |
| 42 | +- Always dispose `DotNetObjectReference` instances in Dispose methods |
| 43 | +- Track event listeners in JavaScript and provide cleanup functions |
| 44 | +- Call JavaScript cleanup functions before disposing DotNetObjectReference |
| 45 | +- Use Map or similar structures to track handlers for proper cleanup |
| 46 | +- Example pattern: |
| 47 | + ```javascript |
| 48 | + const _handlers = new Map(); |
| 49 | + function setupListener(element, event, handler) { |
| 50 | + _handlers.set(`${event}-${element.id}`, handler); |
| 51 | + element.addEventListener(event, handler); |
| 52 | + } |
| 53 | + function cleanup() { |
| 54 | + _handlers.forEach((handler, key) => { |
| 55 | + const [event, elementId] = key.split('-'); |
| 56 | + document.getElementById(elementId)?.removeEventListener(event, handler); |
| 57 | + }); |
| 58 | + _handlers.clear(); |
| 59 | + } |
| 60 | + ``` |
| 61 | +
|
| 62 | +### Async Operation Management |
| 63 | +- Avoid fire-and-forget async operations (`_ = SomeAsyncMethod()`) |
| 64 | +- Use `Task.Run()` with proper error handling for background operations |
| 65 | +- Always handle exceptions in background tasks |
| 66 | +- Use `CancellationToken` for long-running operations |
| 67 | +- Example safe pattern: |
| 68 | + ```csharp |
| 69 | + Task.Run(async () => { |
| 70 | + try { |
| 71 | + await SomeAsyncOperation(); |
| 72 | + } catch (Exception ex) { |
| 73 | + // Log error appropriately |
| 74 | + } |
| 75 | + }); |
| 76 | + ``` |
| 77 | +
|
| 78 | +### Event Handler Cleanup |
| 79 | +- Remove all DOM event listeners in cleanup functions |
| 80 | +- Use AbortController for modern event listener cleanup when possible |
| 81 | +- Clean up MutationObserver instances |
| 82 | +- Remove global event handlers (window, document) on component disposal |
| 83 | +
|
| 84 | +### Service Disposal Best Practices |
| 85 | +- Implement disposal guards in all public methods of disposable services |
| 86 | +- Use ConfigureAwait(false) in disposal methods when possible |
| 87 | +- Dispose resources in reverse order of creation |
| 88 | +- Handle disposal of injected services appropriately |
| 89 | +
|
33 | 90 | ## Error Handling |
34 | 91 | - Implement error boundaries |
35 | 92 | - Use try-catch blocks in lifecycle methods |
|
0 commit comments