Skip to content

Commit

Permalink
Merge pull request #193 from stoiandan/master
Browse files Browse the repository at this point in the history
Update 2024-09-16-lazy-loading-in-csharp.md
  • Loading branch information
lucianpatian authored Sep 18, 2024
2 parents e8f0435 + 8cb371e commit 7330e0d
Showing 1 changed file with 11 additions and 9 deletions.
20 changes: 11 additions & 9 deletions _posts/2024-09-16-lazy-loading-in-csharp.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
_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.
- Memory footprint – RAM is still important, avoiding loading a heavy object in memory 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:
While the theory sounds great, there still remains the question of 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_:
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 code, often get initialized early on. For example in `.NET Framework` _static_ fields get initialized before the 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
Expand All @@ -32,14 +32,14 @@ First, when it comes the the [_singleton pattern_](https://en.wikipedia.org/wiki
}
```

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.
In the above example ☝️ we can see how an `if` statement is used to check if our singleton has ever been instantiated 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.
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 Framework`) come with build-in support, namely: `Lazy<T>`, a generic class which can wrap objects of any type and delay their 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#
Expand All @@ -59,13 +59,15 @@ public class Foo
```
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:
From the get go, our solution needs to address any possible type, not just `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:
Second, we'd need to know all about how _exactly_ to create this `bar` object, and yet, _delay_ the process… This sounds like a _producer_ – a method that produces such and object, encorporates the _how_ – and a _callback_ (_lambda_ or _higher-order function_), a method passed as argument, to be called when needed. This is exactly what we need! In `.NET` the `Func<T>` type holds a _method_ (or _function_ for you functional programmers) that takes no parameters and returns a `T``Func` comes in a buch of variations that also take arguments, the last one always representing the return value, such as `Func<T,TResult>`, `Func<T,T,TResult>` and so on.

We can use is as such:

```C#
public class Lazy<T>
Expand All @@ -89,7 +91,7 @@ public class Foo
}
```

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:
This looks great! Now, whenever we want our 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
Expand All @@ -105,4 +107,4 @@ 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.
It's true that now we can't really do an in-line initialization, but that would have been true even without our static wrapper, unless, of-course, we use static variables.

0 comments on commit 7330e0d

Please sign in to comment.