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:
- The two DateCreated values will not be the same (off by probably only a few nano or milliseconds, but still off)
- 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 using
s - 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:
- Explicitly dispose of resources used when going out of scope
- Make use of the
using
syntax to be able to create a create a scoped entity (as in the above examples)
References
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.