What are Closures in C# and why you NEED to know about them

  Рет қаралды 74,257

Nick Chapsas

Nick Chapsas

3 жыл бұрын

Become a Patreon and get source code access: / nickchapsas
Check out my courses: dometrain.com
Hello everybody I'm Nick and in this video I will explain in plain English, what is a closure in C# and why you need to know about them. When it comes time to optimize your code for memory allocations, closures tend to be the usual suspect for a big chunck of your allocated memory. Let's see what they are and how we can fix them.
Don't forget to comment, like and subscribe :)
Social Media:
Follow me on GitHub: bit.ly/ChapsasGitHub
Follow me on Twitter: bit.ly/ChapsasTwitter
Connect on LinkedIn: bit.ly/ChapsasLinkedIn
Keep coding merch: keepcoding.shop
#dotnet #csharp #asyncawait

Пікірлер: 124
@uncommonbg
@uncommonbg 3 жыл бұрын
Thank you for the informative video, Nick. I would suggest that you should also do a video showing the advantages of Closures. Closures are not always bad and they can be very useful in certain situations.
@spectre0014
@spectre0014 2 жыл бұрын
Oh wow. Finding out about clrheap alone was totally worth watching the whole video. You did a good job explaining this and doing the walk through as well. Thanks for this.
@brandonpearman9218
@brandonpearman9218 3 жыл бұрын
Nice vid. That ClrHeapAllocationAnalyzer is a great tip. Thanks
@merthyr1831
@merthyr1831 2 жыл бұрын
Good preface on the video about premature optimisation. It's something as a newbie programmer I struggled with when 99% of my projects would never need high performance! And that 1% where performance was an issue, it's because of something much more simple than advanced topics. Still, I love seeing indepth looks into how C# works !
@karsh001
@karsh001 3 жыл бұрын
Learned something new today. Thanks for that!
@othmanteffahi7788
@othmanteffahi7788 3 жыл бұрын
I love c# very much and i like people who help us to improve our knowledge, thank you so much bro . still with us 🙏🙏😁😁
@pqsk
@pqsk 2 жыл бұрын
Beautiful video. Something I have never considered in all my years coding in c#
@PainFireFist
@PainFireFist 3 жыл бұрын
I am using Linq lambda expressions all the time and was not aware of closures. Even though my applications run fine, I might be able to optimize memory usage with that new knowledge. Thanks!
@giovannitresoldi5420
@giovannitresoldi5420 3 жыл бұрын
Very informative as usual. Thank you very much!
@easycodeunity3d14
@easycodeunity3d14 2 жыл бұрын
Thank you very much, Nick. Great job!
@M3rken
@M3rken 3 жыл бұрын
Great video Nick!
@Ziberac
@Ziberac 3 жыл бұрын
Awesome content. Very useful for coding on IOT devices.
2 жыл бұрын
Awesome tip. Thank you for sharing
@onlyrock1351
@onlyrock1351 2 жыл бұрын
Really nice explanation, tnx
@Rushhourz0
@Rushhourz0 3 жыл бұрын
I think another problem with this is, that from functional programming you learn that you should keep your functions as pure as possible (not influencing or being influenced from stuff outside the function). So a more correct lambda would be ...(x, number => x>number); //if that would even work it is So I wonder if that scenario would also allocate on the heap
@BrokoIis
@BrokoIis 3 жыл бұрын
very good tip! this should not create a closure. of course this would not work in this exact situation, but still.
@berylliosis5250
@berylliosis5250 3 жыл бұрын
Here it wouldn't work, and it wouldn't make a closure if it did, but you're missing an important point: closures are still pure functions. Indeed, closures as a concept are taken from functional programming. Consider: a fundamental functional construct is currying, which makes functions with multiple arguments into functions with one argument that return functions. Closures are required for currying, because the parameters from the higher scope need to be passed to the parameters of the lower scope. So: closures are pure functions, as long as you don't mutate. So the example in the video is pure, but "x => x > number++" is not a pure function because it mutates the value of number, and therefore can provide different outputs for the same input
@wtfitsdrewbritton
@wtfitsdrewbritton 2 жыл бұрын
This is a good suggestion for sure, keeping loose coupling between separate functions. If anything it makes debugging less stressful later on 🙃
@ivandrofly
@ivandrofly 7 ай бұрын
There is also Heap Allocation Viewer in Rider plugin to catch closure and more when allocation is happening
@paulecampbell
@paulecampbell 3 жыл бұрын
nice video young chap!
@CharlesBurnsPrime
@CharlesBurnsPrime 2 жыл бұрын
This is the in-depth geekery that I live for.
@microtech2448
@microtech2448 3 жыл бұрын
How that function is being called when you have taken off the variable from arguments list?
@pdn9609
@pdn9609 3 жыл бұрын
Ωραίος φίλε Νίκο
@jez9999
@jez9999 3 жыл бұрын
This is kind of a bummer because the "fix" is to just make your code more low level and less readable. I wish it were optimized automatically.
@Gastell0
@Gastell0 3 жыл бұрын
Not low enough, where's unsafe keyword? xD
@ivaniliev93
@ivaniliev93 2 жыл бұрын
Yep when you write such lambda closures in C++ you have to write what should be captured and how (e.g. by ref or value)
@semen083
@semen083 3 жыл бұрын
It is any way to avoid allocation in your example with passing argument in another method?
@shahzaibhassan2777
@shahzaibhassan2777 Жыл бұрын
Hey Nick, you seem to be future Tim Corey Fantabulous!
@cdarrigo
@cdarrigo 3 жыл бұрын
Is there a way to pass an argument into the lands so it doesn't need the closure?
@jongeduard
@jongeduard Жыл бұрын
That "lexical" thing is really from the wikipedia article about closures and this is certainly not the first video from someone where I hear criticism on that. That article seems to have improved a bit compared to how bad it was earlier, but it's just crazilly complex explanation of something very simple. The feature is most well known from JavaScript, but C# can do the same thing indeed and C++ has even a very advanced implementation in which you can make very precise decisions about what variables you exactly want to capture from the scope around and how (pointer, referene, etc).
@user-bk7pc7nh2n
@user-bk7pc7nh2n 3 жыл бұрын
Another useful video Niko. Thank you. These things can blow out of proportion in an environment loaded with asynchronous calls. Also, const-ness is something I am really missing in C#, having had a (bitter-sweet) taste of C++ development. And I find that c# developers lack the consideration (or discipline?) to set things to const if they are such. They thing that it is an overkill.
@berylliosis5250
@berylliosis5250 3 жыл бұрын
It's mostly because C#'s readonly is trash. const is good for compile-time constant value types (not sure if structs work with it?), but readonly is pretty much useless because it only prevents changing which object you refer to - you can always mutate the object pointed to. It blew my mind how much I'd been missing when I first used a language with immutability by default.
@timur2887
@timur2887 8 күн бұрын
I wonder why the outer scope of closure is passed in as a class instead of passing it via additional parameters to a callback
@MetaArcher
@MetaArcher Жыл бұрын
do you have another video on how to tackle this issue? Like imagine I still want it to keep number as a parameter and use delegates and get rid off closures, is that possible?
@StockportJambo
@StockportJambo 3 жыл бұрын
Nice vid and explanation. BTW, how long do you have to be a Patreon for before your name appears in the acknowledgments at the end? Mine isn't there.
@nickchapsas
@nickchapsas 3 жыл бұрын
Hey Bil. Because I pre-record many videos in batches it might be that it is 2 weeks before your name starts appearing. I also publish many videos out of order of recording so it could be that it starts appearing and then disappears for a few videos and then starts appearing again.
@BigBang1112tm
@BigBang1112tm 3 жыл бұрын
Fun to see about a thousand warnings after installing that package xd
@berylliosis5250
@berylliosis5250 3 жыл бұрын
7:30 Why does it still allocate? Is it a LINQ internal allocation for some reason, or is it a language-level alloc?
@fedormorozov8255
@fedormorozov8255 2 жыл бұрын
I wonder if I make a field _savedNumber in class and put the number value to that field and use the field in lambda it would still allocates memory?
@nathaaaaaa
@nathaaaaaa 3 жыл бұрын
Yes, I'm into functional programming. How could you tell?
3 жыл бұрын
It's a boxing effect ? Compiler optimization will fix it ?
@andriet
@andriet 3 жыл бұрын
Hey Nick, what IDE are you using on the video?
@dsedchenko
@dsedchenko 3 жыл бұрын
It's Jetbrains Rider
@brunoccs
@brunoccs 2 жыл бұрын
What's the alternative?
@killer55909
@killer55909 3 жыл бұрын
Great Video, I enjoyed it very much, however I have a small question. When Reading the C# In Depth it struck me that Closures reminded me of Captured Variables. Am I wrong when I assumed that Captured Variables in C# is the same as Closures?
@nickchapsas
@nickchapsas 3 жыл бұрын
Yeah captured variables are captured in a closure class
@killer55909
@killer55909 3 жыл бұрын
@@nickchapsas Awesome, thanks for the answer :D
@crazyfox55
@crazyfox55 3 жыл бұрын
You should add a video about how this affects the new record type. I imagine that a class gets created to hold a copy of the record, but maybe its just a reference instead of a copy.
@nickchapsas
@nickchapsas 3 жыл бұрын
The record type is just a class like any other with the only difference that a bunch of code will be lowered to support the equality checks. It's just synstactic sugar around it's principles. The record is a full copy.
@crazyfox55
@crazyfox55 3 жыл бұрын
@@nickchapsas I thought records were passed by reference though. I need to do more research on when they are copied.
@nickchapsas
@nickchapsas 3 жыл бұрын
@@crazyfox55 They are passed by reference yeah. I thought you were talking about the with operator that allows "manipulation" but rather creates a whole new object.
@crazyfox55
@crazyfox55 3 жыл бұрын
@@nickchapsas okay cool, so it should just be like how classes are handled. Therfore less overhead than storing a whole new copy of the record.
@ghelyar
@ghelyar 3 жыл бұрын
The real issue that closures can cause is that they can hold on to references, preventing the garbage collector from cleaning them up, in a similar way to event handlers. Also if you have more than one lambda in a method they can capture more than you might expect. A few bytes of memory here or there doesn't generally matter too much, but a lot of allocations means a lot of garbage collections, or a reference being kept can prevent a large tree from being collected. For example you might have a CancellationTokenSource with some references to CancellationTokenRegistrations, with closures that capture an object that contains a MemoryStream, which keeps a reference to it's full buffer even after being disposed. Of course the CTS and CTR are disposable, so make sure to dispose them for this particular case, but it's just one example.
@antoruby
@antoruby 3 жыл бұрын
“The advantage of GC is that you don’t need to manage memory anymore” - does anyone still believe this? :p Good points anyhow.
@ErnestoChavesChaves
@ErnestoChavesChaves 3 жыл бұрын
Which IDE is that? vscode?
@clearlyunwell
@clearlyunwell 3 жыл бұрын
👍
@mariusopr
@mariusopr Жыл бұрын
What if you use readonly instead of const?
@sporadics
@sporadics 3 жыл бұрын
Hi, Nick...I have been watching your videos. Some of them are ready great. Thank you. Just a friendly suggestion, can you slow down half of notch, so I can better understand you? :)
@snuffsix9598
@snuffsix9598 2 жыл бұрын
You can always play the video at slower speed?
@lizard450
@lizard450 3 жыл бұрын
Love your videos. Here's a video idea... I've been working with visual studio for years and never really learned how to read the intelisense documentation. I mean for most stuff it's no issue but i have trouble reading how to call Dictionary with linq or selectmany etc. I just either google these features or remember them. It'd be neat to be able to read the intelisense documentation
@buttonasas
@buttonasas 3 жыл бұрын
What if you use a larger object instead of an int? It seems like it keeps just the value of the int instead of a reference... for some reason?
@nickchapsas
@nickchapsas 3 жыл бұрын
It allocates an int in a closure class as a class member which is a reference type allocation on the heap.
@keyboard_g
@keyboard_g 2 жыл бұрын
Sounds like this display class issue can be solved by the compiler. Maybe force it on the stack with a record instead of a full class.
@nickchapsas
@nickchapsas 2 жыл бұрын
Records are lowered to classes. They are not value types and even if they where, value types are not guaranteed to live on the stack unless they are ref structs
@adamding3873
@adamding3873 3 жыл бұрын
This is a lamda expression, or the arrow function. If you use some variable from the caller's scope, then it must be generated on the fly. It does not only consume more memory, but also consume more CPU because it has to be re-compiled, because each function is a different instance. This is a really a hidden perf killer. Some developers may not realize it when they write the "bad" code.
@buttonasas
@buttonasas 3 жыл бұрын
No it mustn't? The variables are mere parameters for the lambda, they don't _need_ to be compiled during runtime. I don't know how it is in practice for C# but it sounds like nonsense.
@adamding3873
@adamding3873 3 жыл бұрын
@@buttonasas It uses the parent scope's variable as part of its implementation, not an input parameter. So unless the lambda has a hidden context parameter referring to its parent scope and all its local variables, it has to be created after the parent scope is created.
@buttonasas
@buttonasas 3 жыл бұрын
@@adamding3873 Method functions also have to be "created" after their parent class is "created". Perhaps, we are using different meanings for "compiled" and "created"?
@adamding3873
@adamding3873 3 жыл бұрын
@@buttonasas Method functions are the same for all instances of a class. So they are compiled once and cached for future use. A Lambda function w/o dependency on the parent scope, is also the same for all instances. So it is also compiled only once. However, with the dependency on the parent scope, it has to be compiled along with the scope. The parent scope, usually a function scope, starts when the function is called and ends when the function returns. So the lambda function is compiled after the parent function is called, and discarded when the function is returned.
@buttonasas
@buttonasas 3 жыл бұрын
@@adamding3873 "Method functions are [...] compiled once and cached..." - do you mean that happens during runtime? I hope that isn't the case. ("cache" isn't a great word for it if it's never cleared, not even on system shutdown) I don't see why you couldn't reuse the lambdas in a similar fashion. And am still very sure some languages do reuse them just fine.
@sexcommunist
@sexcommunist 3 жыл бұрын
JavaScript: *sweating uncontrollably* (In js every function is a clusure)
@artemivanov2141
@artemivanov2141 3 жыл бұрын
Does visual studio have memory tab like in rider?
@AlexanderYaremchuk
@AlexanderYaremchuk 9 ай бұрын
yeah, I found it - Debug -> Windows -> Managed Memory take a snapshot open it by clicking link in the table and here you're - similar window with filter by type
@artemivanov2141
@artemivanov2141 9 ай бұрын
@@AlexanderYaremchuk thanks alot!
@ronaldoperes1202
@ronaldoperes1202 2 жыл бұрын
How can I see that 'Memory Window' using Visual Studio?
@nickchapsas
@nickchapsas 2 жыл бұрын
I haven't used VS in so many years that I don't quite remember but I think there is something. Maybe someone can help with this one
@ibrahimhussain3248
@ibrahimhussain3248 3 жыл бұрын
Does this significantly effect performance when the system has loads of RAM?
@nickchapsas
@nickchapsas 3 жыл бұрын
The only way in which it does is by affecting memory allocation which in return affect garbage collection and garbage collection can affect performance. It is not something that you should worry about but it’s something you should be aware of
@paulkoopmans4620
@paulkoopmans4620 3 жыл бұрын
Also.. loads of ram will not help. Even if your machine has 8GB it will not be available if you run a 32bit process for example. Only 2GB max for a process. That has to include all of the memory the CLR needs for housekeeping, so you would never hit that 2GB.
3 жыл бұрын
What IDE is that ?
@nickchapsas
@nickchapsas 3 жыл бұрын
This is JetBrains Rider
@Name-kj8ew
@Name-kj8ew 3 жыл бұрын
Can somebody tell me, what is the IDE he works in?
@nickchapsas
@nickchapsas 3 жыл бұрын
That’s JetBrains Rider
@maorhamami8106
@maorhamami8106 3 жыл бұрын
C# keep surprizing me. I was expecting the compiler to do it for me. If i recall java lamda only allow "final" variables to avoid that...
@ScottBlomquist
@ScottBlomquist 3 жыл бұрын
Java's requirement for closure-captured variables to be final doesn't avoid allocating closure objects to store captured values. Instead, it avoids having to worry about changes to the captured values leaking into or out of the closure.
@applepie7282
@applepie7282 2 жыл бұрын
closures are great, C#'s weak reference system is really sucks. I miss Swift 's weak-strong operators.
@kimkimpa5150
@kimkimpa5150 2 жыл бұрын
Been programming C# since forever. Watched the whole video. Still haven't got the foggiest idea what a closure is :P
@KoScosss
@KoScosss 3 жыл бұрын
Hair update 👀👍
@CrapE_DM
@CrapE_DM 3 жыл бұрын
You do realize it's always making a class, right? It doesn't get named with "Display" because it's not closing over a value, but it's still there as a Singleton.
@nickchapsas
@nickchapsas 3 жыл бұрын
This is partially correct. It's always creating a class but it's creating the display class on top of this to allocate value types inside that class, which is the problematic class. The class allocation isn't the problem. The captured variables allocation is. If you check the lowered code, the class created in the non-closure version is fundamentally doing something different than the Display class. One is holding the variables and has the delegate and is being initialized to re-allocate.
@alissonreinaldosilva1119
@alissonreinaldosilva1119 3 жыл бұрын
Imagine chaining multiple LINQ calls
@kimkimpa5150
@kimkimpa5150 2 жыл бұрын
I do it all the time
@thebloxxer22
@thebloxxer22 3 жыл бұрын
There are 2 types of people, Those who like closure.
@ezra3871
@ezra3871 3 жыл бұрын
Huh never seen someone use rider on any youtube video
@bongangcobo
@bongangcobo Жыл бұрын
numbers numbers.....mmh numbers ! lol XD
@modernkennnern
@modernkennnern 3 жыл бұрын
Shouldn't the function be called "NumberOfNumbersOverNumber"? 🤔
@nickchapsas
@nickchapsas 3 жыл бұрын
Sure
@Yosso117
@Yosso117 Жыл бұрын
Братан, хорош, давай, давай, вперёд! Контент в кайф, можно ещё? Вообще красавчик! Можно вот этого вот почаще?
@WhiteDragon103
@WhiteDragon103 2 жыл бұрын
This is one of the more frustrating things about C#. A whole bunch of language features are needlessly rendered unusable for performance-critical code when the compiler could easily convert the .Count() method into a for loop with 0 allocations behind the scene.
@WilliamLDeRieuxIV
@WilliamLDeRieuxIV 3 жыл бұрын
An anonymous lambda is compiler syntactic sugar that is meant to reduce the amount of code you have to write....the compiler will expand it for you (which could include inserting classes and methods). Solution....don't rely on compiler syntactic sugar and just write the code yourself.
@kimkimpa5150
@kimkimpa5150 2 жыл бұрын
More code = more maintenance cost though. So it's a tradeoff.
@WilliamLDeRieuxIV
@WilliamLDeRieuxIV 2 жыл бұрын
@@kimkimpa5150 The maintenance cost can also be increased by using these compiler convenience-features (by letting the compiler assume what was intended -- often getting it wrong). This leads to errors and bugs that can only be caught runtime through code debugging (including in that is the variety of systems and environments that the code might running in). All of that could be avoided by the programmer explicitly writing the code rather than relying on the compiler to figure it out.
@kimkimpa5150
@kimkimpa5150 2 жыл бұрын
@@WilliamLDeRieuxIV Yes, but the means by which we deconstruct our closures are arguably error prone in other areas since we have more code to deal with. At least with a short and sweet lambda closure, the overall amount of code is reduced, and the memory issue is both manageable, and as the video showed, statically analyzable at compile time. So I'm saying it's a fairly equal tradeoff.
@CostaKazistov
@CostaKazistov 3 жыл бұрын
Audio/video out of sync
@eramires
@eramires 3 жыл бұрын
There is never anything that makes your life easier, that does not have a cost in performance. Every shortcut, has it's price. People need to get this on their heads. :-\ Lazy coding, will always cost you something. :)
@igorthelight
@igorthelight 3 жыл бұрын
True!
@ChristopherSalisburySalz
@ChristopherSalisburySalz 3 жыл бұрын
The more videos I watch about C# optimization, the more I think LINQ is just a bad idea. There are so many pitfalls and gotchas.
@MaximilienNoal
@MaximilienNoal 3 жыл бұрын
Meh, it's fine 99.99 percent of the time. The 'pitfalls' are way overblown.
@zokocx
@zokocx 3 жыл бұрын
It's not a pitfall, more is like balancing where really makes sense to use ling (especially chain more methods) and where simple foreach and if statement inside is more readable. I usually go this path when predicate (in Where, Count, Any etc.) is little more complex than simple oneliner.
@BittermanAndy
@BittermanAndy 3 жыл бұрын
Optimising for development time is perfectly sensible, and usually the right thing to do. It's just a question of knowing what to do instead when "usually" breaks down.
@briannielsbergh
@briannielsbergh Жыл бұрын
What's the point? It makes no difference in real life anyway.
@nickchapsas
@nickchapsas Жыл бұрын
Sure it does
@siavashmohammadbeigi9099
@siavashmohammadbeigi9099 10 ай бұрын
I like your videos but this one is so boaring , i could not end it
6 C# keywords you (probably) never had to use
17:33
Nick Chapsas
Рет қаралды 67 М.
Can You Draw A PERFECTLY Dotted Line?
00:55
Stokes Twins
Рет қаралды 84 МЛН
1❤️
00:17
Nonomen ノノメン
Рет қаралды 8 МЛН
Khó thế mà cũng làm được || How did the police do that? #shorts
01:00
The purest coding style, where bugs are near impossible
10:25
Coderized
Рет қаралды 897 М.
Dependency Injection, The Best Pattern
13:16
CodeAesthetic
Рет қаралды 757 М.
Writing C# without allocating ANY memory
19:36
Nick Chapsas
Рет қаралды 144 М.
What are record types in C# and how they ACTUALLY work
15:36
Nick Chapsas
Рет қаралды 118 М.
Where are types allocated in .NET and why people get it so wrong
14:35
C# Yield Return: What is it and how does it work?
15:09
Brian Lagunas
Рет қаралды 54 М.
8 await async mistakes that you SHOULD avoid in .NET
21:13
Nick Chapsas
Рет қаралды 309 М.
Programming Terms: Closures - How to Use Them and Why They Are Useful
11:44
C# Delegates & Lambdas Explained
32:43
Raw Coding
Рет қаралды 37 М.