Describe how to ensure proper resource cleanup (e.g., closing database connections, disposing of streams) in complex async methods, regardless of success or failure.

.NET interview question for Advanced practice.

Answer

Ensuring resource cleanup in async methods is critical and can be achieved using try/finally blocks or the using statement. 1. try/finally Block: The finally block is guaranteed to execute after the try block completes, whether it finishes normally or throws an exception. This makes it the standard location for cleanup logic like closing connections or disposing of objects. csharp public async Task ProcessDataAsync() { StreamReader reader = null; try { reader = new StreamReader("file.txt"); var text = await reader.ReadToEndAsync(); // ... process text ... } finally { reader?.Dispose(); } } 2. using Statement: The using statement provides syntactic sugar for a try/finally block. The compiler automatically generates the finally block to call .Dispose() on the object. This is the preferred method for its brevity and clarity. csharp public async Task ProcessDataAsync() { using (var reader = new StreamReader("file.txt")) { var text = await reader.ReadToEndAsync(); // ... process text ... } // reader.Dispose() is called here automatically } 3. await using (C 8+): For resources that require asynchronous cleanup (implementing IAsyncDisposable), you can use await using. This ensures that the asynchronous DisposeAsync() method is called and awaited before the method continues. csharp public async Task ProcessDataAsync() { await using (var dbContext = new MyDbContext()) { var data = await dbContext.Users.ToListAsync(); // ... process data ... } // await dbContext.DisposeAsync() is called here }

Explanation

C 8 introduced await using, which works with types that implement IAsyncDisposable. This allows for asynchronous cleanup logic within a finally block, such as flushing a network stream before closing it.

Related Questions