Using with IDisposable

Use using by implementing a single interface

Home DailyDrop

Daily Knowledge Drop

To make use of the using statement in C#, all you need to do is implement the IDisposable interface on a class.

The using statement provides a convenient syntax to ensure the correct use of IDisposable objects. The object in question will exists for the scope of the using and then automatically be disposed once out of scope.

This functionality can also be leveraged to create scoped helper instances for certain use cases.


Examples

All the example below make use of a simple Order with multiple OrderLines entity structure.

class Order
{
    public Guid Id { get; set; }

    public DateTime DateCreated { get; set; }

    public OrderLine[] Lines { get; set; }

    // Provide an convenient way to output the order
    public override string ToString()
    {
        return JsonSerializer.Serialize(this, 
            new JsonSerializerOptions { WriteIndented = true });
    }
}

class OrderLine
{
    public Guid Id { get; set; }

    public DateTime DateCreated { get; set; }

    public Guid OrderId { get; set; }
}

When an Order is created, the OrderLines should be created at the same time. Both entities are required to have the same DateCreated value and we also require the OrderLine.OrderId to be set to the Order.Id

For something so relatively simple, there are a couple of issues.

See the code below which DOES NOT COMPILE:

var order = new Order
{
    Id = Guid.NewGuid(),
    DateCreated = DateTime.Now,
    Lines = new[]
    {
        new OrderLine
        {
            Id = Guid.NewGuid(),
            DateCreated = DateTime.Now,
            // How to set this???
            OrderId = ???
        }
    }
};

The issues:

  1. The two DateCreated values will not be the same (off by probably only a few nano or milliseconds, but still off)
  2. There is no way to easily set the OrderId value on the OrderLine entity

Variable snapshot

These issues can quite easily be solved by using variables to snapshot values before the entity creation:

// snapshot values to be used later
var datetimeCreated = DateTime.Now;
var orderId = Guid.NewGuid();

var order = new Order
{
    // use the snapshot orderId
    Id = orderId,
    // use the snapshot date created
    DateCreated = datetimeCreated,
    Lines = new[]
    {
        new OrderLine
        {
            Id = Guid.NewGuid(),
            // use the snapshot date created
            DateCreated = datetimeCreated,
            // use the snapshot orderId
            OrderId = orderId
        }
    }
};

This will 100% work and is completely acceptable to do - however a cleaner way, which makes use of the using statement.


Using snapshot

First thing is to define a OrderInfoSnapshot class which implements IDisposable:


// Implement IDisposable
class OrderInfoSnapshot : IDisposable
{
    private DateTime _created;
    private Guid _orderId;

    public DateTime Created => _created;

    public Guid OrderId => _orderId;

    // On creation of an instance, we 
    // snapshot the values we are interested in
    public OrderInfoSnapshot()
    {
        _created = DateTime.Now;
        _orderId = Guid.NewGuid();
    }

    // Implement the IDisposable Dispose method. This is a very 
    // simple example, but more sophisticated cleanup can happen here
    public void Dispose()
    {
        // do cleanup etc here
        // look at resources for correct
        // Dispose usage
    }
}

Now instead of using a number of variables for the various snapshots required, OrderInfoSnapshot can be used with a using statement:

// create the snapshot
using (var orderInfo = new OrderInfoSnapshot())
{
    var order = new Order
    {
        // grab the OrderId from the snapshot
        Id = orderInfo.OrderId,
        // Create from the snapshot
        DateCreated = orderInfo.Created,
        Lines = new[]
        {
            new OrderLine
            {
                Id = Guid.NewGuid(),
                // Create from the snapshot
                DateCreated = orderInfo.Created,
                // OrderId from the snapshot
                OrderId = orderInfo.OrderId
            }
        }
    };

    Console.WriteLine(order);
}

The OrderInfoSnapshot will automatically be disposed (which in this case doesn't need to do much cleanup) once the end of the using scope is reached.

As mentioned, this is a very simple example, but if the OrderInfoSnapshot was required to access multiple other resources, all the connections could be cleaned up in the Dispose method.


Alternative syntax

Introduced in C# 8, there is an alternative syntax for usings - the end result and operation is exactly the same though.

// create the snapshot
using var orderInfo = new OrderInfoSnapshot();

var order = new Order
{
    Id = orderInfo.OrderId,
    DateCreated = orderInfo.Created,
    Lines = new[]
    {
        new OrderLine
        {
            Id = Guid.NewGuid(),
            DateCreated = orderInfo.Created,
            OrderId = orderInfo.OrderId
        }
    }
};

Console.WriteLine(order);

The scope of the OrderInfoSnapshot instance is now the method it was created in, and it will still automatically be disposed when going out of scope.


Notes

Implementing IDisposable correctly is a bit more complicated than shown above - see the references for more details.


Conclusion

The IDisposable is not required on every class, but it makes sense and can be useful when you want to:

  1. Explicitly dispose of resources used when going out of scope
  2. Make use of the using syntax to be able to create a create a scoped entity (as in the above examples)

References

IDisposable Interface

Daily Drop 13: 17-02-2022

At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.

The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.
c# .net idisposable using disposable