Daily Knowledge Drop
The loading of (large) objects can be deferred
until they are actually used and required
using the Lazy<>
class
Sample
Use case
In our use case, we have a FileEntity
which contains details about a file in a specific location. There are two child entities which are properties to FileEntity
:
FileSize
: stores the file size in bytes (and just stores a double value, so is small)FileContents
: stores the contents of the file as a string (depending on the size of the file, this can obviously be very large)
The contents of the file will not always be used
and are potentially very large
- so let's look at how we can defer loading the data until it is actually used and required.
The setup
// the main file entity class
public class FileEntity
{
// a normal FileContentsEntity private variable,
// just wrapped in Lazy<>
private readonly Lazy<FileContentsEntity> _fileContents;
public Guid Id { get; }
public string FileLocation { get; }
public FileSizeEntity FileSize { get; }
// when accessed, return Lazy<FileContentsEntity>.Value
public FileContentsEntity FileContents => _fileContents.Value;
public FileEntity(Guid id, string fileLocation, double fileSizeInBytes)
{
Console.WriteLine("FileEntity constructed");
Id = id;
FileLocation = fileLocation;
FileSize = new FileSizeEntity(fileSizeInBytes);
// instead of instantiating the fileContents,
// instantiate Lazy<> with an startup method
_fileContents = new Lazy<FileContentsEntity>(LoadFileContents);
}
// method to load the large data volume
private FileContentsEntity LoadFileContents()
{
return new FileContentsEntity(FileLocation);
}
}
// simple entity to store the file size
public class FileSizeEntity
{
public double FileSizeInBytes { get; set; }
public FileSizeEntity(double fileSizeInBytes)
{
Console.WriteLine("FileSizeEntity constructed");
FileSizeInBytes = fileSizeInBytes;
}
}
// simple entity to store the file contents
public class FileContentsEntity
{
public string LargeStringValue { get; set; }
public FileContentsEntity(string fileLocation)
{
Console.WriteLine($"FileContentsEntity loaded from '{fileLocation}'");
LargeStringValue = "LargeStringValue";
}
}
To use Lazy<>
, the setup is almost exactly the same as without using Lazy<>
.
Output
So what does the Lazy<>
actually enable, and how does it effect execution?
Console.WriteLine("== pre initialization ==");
// Two FileEntity instances are created,
// one with a small file and one with a large file
var file1 = new FileEntity(Guid.NewGuid(), @"C:\small-file.txt", 100);
var file2 = new FileEntity(Guid.NewGuid(), @"C:\large-file.txt", 1073741824);
Console.WriteLine("== post initialization ==");
Console.WriteLine("");
// The large FileContents is accessed
Console.WriteLine("Accessing file1 contents");
var file1Location = file1.FileContents;
// only the FileLocation is accessed
Console.WriteLine("Accessing file2 location");
var fileContents = file2.FileLocation;
The output looks as follows:
== pre initialization ==
FileEntity constructed
FileSizeEntity constructed
FileEntity constructed
FileSizeEntity constructed
== post initialization ==
Accessing file1 contents
FileContentsEntity loaded from 'C:\small-file.txt'
Accessing file2 location
The key take-away from the output, is that the large contents of file2 are never loaded
, as they are never used.
Thread safety
There are additional considerations regarding thread safety and Lazy<>. These are not addressed in this post, but can be read in details in the reference below.
Notes
Lazy<>
provides a simple and easy to use way to load objects only when they are uses - this is especially useful when the large object is not loaded on every code path. This can lead to better performance and memory usage.
References
Daily Drop 27: 09-03-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.On This Page