Skip to content

Commit

Permalink
Merge pull request #192 from stoiandan/master
Browse files Browse the repository at this point in the history
wrote C# Lazy loading post
  • Loading branch information
xeeo authored Sep 17, 2024
2 parents 3c9b985 + 22f3d92 commit e8f0435
Showing 1 changed file with 108 additions and 0 deletions.
108 changes: 108 additions & 0 deletions _posts/2024-09-16-lazy-loading-in-csharp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Lazy Loading in C# (and not only)

## Introduction
_Lazy loading_ is a technique used to delay the execution of code for later on. There are a couple of reasons as to why you'd want to do that, and we'll enumerate some:

- Speed – the best code, is also the fastest code, the most secure and most maintainable… namely _no code at all_. However, for obvious reasons, we do need at times to write code, but while we can't avoid writing it, we could delay or even, _possibly_, completely avoid _executing_ it.
- Memory footprint – RAM is still important, avoiding loading a heavy object does not just result in increased speed, but also in a more efficient system, memory-wise.

## Hands-on

While the theory sounds great, there still remains the question how do you actually _avoid_ calling code? While there are a couple of ways to do that, I'd like to focus on two of them:


### Singletons
First, when it comes the the [_singleton pattern_](https://en.wikipedia.org/wiki/Singleton_pattern), objects (if we're talking in an _OOP_ context, but this goes for other paradigms as well) are often _statically_ allocated. This affects us even more, as static objects, often get initialized early on. For example in `.NET Framework` _static_ fields get initialized before the non-static constructor of the class is called. This is also the case for other platforms, like `Java`. Here an `if` check is usually employed for _lazy loading_:

```C#
class Singleton
{
private static Singleton _instance = null; // redundant, for explicity
private Singleton { ... }

public Singleton Instance()
{
if (Singleton._instance == null)
{
_instance = new Singleton();
}
return _instance;
}
}
```

In the above example ☝️ we can see how an `if` statement is used to check if our singleton has ever been istanciated before, if so, we just return that instance. However, if this is the first time, we create a first instance. This saves us some time and memory, because we might not get to use the class at all (depending on the use case) or we might just delay the execution.

Notice, the more moder `.NET` platform actually uses lazy loading by default. Static fields only get initialized before being used, and so, `.NET` does this for us, as opposed to the older `.NET Framework`.


## Instance fields

The more interesting and common use case, is when we'd like to delay the initialization of an _instance_ field. For this, both frameworks (`.NET` and `.NET Framwork`) come with build-in support, namely: `Lazy<T>`, a generic class which can wrap any object and delay it's initialization. We'll explore a simple implementation of such an idea, and see how we can achieve this in pretty much any programming language.

Frist imagine the scenario of two classes:
```C#
public class Foo { ... }
```
and
```C#
public class Bar { ... }
```
with a _composition_ relationship of type:
```C#
public class Foo
{
Bar bar = new();
...
}
```
Our goal is to delay the initialization of the field `bar` in any `Foo` instance.

From the get go, our solution needs to address any possible type, not just field of type `Bar`. This is why _generics_ are needed. So our solution begins to look as such:

```C#
public class Lazy<T> { … }
```

Second, we'd need to know all about how _exactly_ to create this _delayed_ `bar` object, and yet, not create it just yet… This sounds like a _producer_ and a _callback_ (or _higher-order function_) is exactly what could help us. In `.NET` the `Func<T>` type is a _function_ type that takes 0 parameters and returns a `T`. We can use is as such:

```C#
public class Lazy<T>
{
private Func<T> _generator;
public Lazy(Func<T> generator) => _generator = generator;

public T Load() => this._generator();

}
```
All that we're providing to the constructor of the `Lazy` class is a function, that describes how to return a `T`, and whenever we want to actually return that `T`, we just call `Load`.

Let's see this in action:
```C#
public class Foo
{
// old code: Bar bar = new();
Lazy<Bar> bar_lazy = new Lazy<Bar>(() => new());
...
}
```

This looks great! Now, whenever we want our object instance, we can just call `Load` on `bar_lazy`, but the beauty of warping things in a function is that, even if our `Bar` constructor required parameters, this would be fixed with just a small adjustment:

```C#
public class Foo
{
//ctor injection, provide `lazy_bar` as a dependency
Foo(Lazy<Bar> lazy_bar)
...
}
```
```C#
// calling code
lazy_bar = new Lazy<Bar> (() => new Bar(name, age, other_param))
new Foo(lazy_bar);
...
```
It's true that now we can't really do an in-line initialization, but that woul have been true even without our static wrapper, unless, of-course, we use static variables.

0 comments on commit e8f0435

Please sign in to comment.