Modern C# · Lesson 24

Deterministic cleanup with using

Release an owned resource when control leaves its scope.

How it works

Some objects own resources that need deterministic cleanup, such as a stream handle or a temporary registration. IDisposable exposes that cleanup operation. A using declaration arranges for Dispose to run when control leaves the enclosing scope, rather than waiting for the garbage collector to reclaim the object.

The sample's Lease prints when disposed so the lifetime is visible without opening a real file. Use creates the lease, prints working, and reaches the end of its scope. Cleanup then prints released. The lifetime belongs to the method that acquired the resource; the caller does not need to remember a separate release instruction.

The same cleanup arrangement applies when control leaves the scope through an exception. Conceptually, the compiler arranges a finally boundary around the owned resource. That does not make exceptions harmless, but it prevents ordinary failure paths from silently skipping cleanup code placed only after the successful calculation.

Do not dispose an object merely because it implements IDisposable if ownership belongs to another component. A method receiving a borrowed resource generally should not close it without an explicit contract. Ownership answers who must clean up; the interface answers how cleanup is requested. Confusing the two can break a caller's later operations.

Practice by throwing an InvalidOperationException after working and catching it in the caller. Predict the order of working, released, and the caller's error message. Then add a second lease and observe the reverse cleanup order when both were acquired in the same scope.

A using declaration lasts until the end of the containing scope, which can be longer than the few statements that need the resource. Use an explicit using block when you want a narrower lifetime. For resources with asynchronous cleanup, investigate IAsyncDisposable and await using rather than blocking an asynchronous operation inside Dispose.

A working example

using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;

Use();
static void Use() {
    using var resource = new Lease();
    Console.WriteLine("working");
}
class Lease : IDisposable {
    public void Dispose() { Console.WriteLine("released"); }
}

Expected output

working
released
Try this
A using declaration disposes at the end of its enclosing scope.