Thanks to all of the functional goodness that was put into C# 3.0 we have a ton of power at our disposal. Unfortunately with a lot of this power also comes increased complexity. Programmers usually exacerbate the complexity problem, because unfortunately we often like to sound like we are smarter than we actually are.
We often like to use big words and lots of acronyms to scare away the neophytes. Oh crap, there I go again…
Anyways, one of these topics that is blindingly simple is closures. But I continue to see definitions that are as clear as mud to the average developer. Let’s go ahead and look at the Wikipedia definition of a closure:
“In computer science, a closure is a first-class function with free variables that are bound in the lexical environment.”
All clear, right? Well, if it is for you, then super… you can stop reading. But if not, and the next time this topic comes up you want to sound like Super Duper Computer Science Guy™ … then keep reading.
Sir, Your Functions Are First Class
So first, what is a "first-class function"? A first class function simply means that it is a function which your language treats as a first class data type. It means that you can assign a function to a variable, pass it around, and invoke it… just like a normal function. In C# we can create a first class function using anonymous methods:
1
2
3
4
|
Func< string , string > myFunc = delegate ( string var1) { return "some value" ; }; |
Or we can do it using lambdas:
1
|
Func< string , string > myFunc = var1 => "some value" ; |
Both of those are functionally equivalent, and they just create a method that takes a string and returns a string. We can call that method by invoking the variable just like we would any method:
1
|
string myVar = myFunc( "something" ); |
This means that C# supports first class functions, yay!
I Like My Variables Free
And so now we have first-class functions with free variables… And what, pray tell, is a free variable? A free variable just happens to be a variable which is referenced in a function which is not a parameter of the function or a local variable of the function. Okay, so it might look like this:
1
2
3
4
5
6
|
var myVar = "this is good" ; Func< string , string > myFunc = delegate ( string var1) { return var1 + myVar; }; |
Okay, so the anonymous delegate is referencing a variable that is in its enclosing scope. That variable isn’t a parameter, and it isn’t a local variable. So it is a free variable. So what?
It Has To Close Over It, Son
So, what happens in this case:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
static void Main( string [] args) { var inc = GetAFunc(); Console.WriteLine(inc(5)); Console.WriteLine(inc(6)); } public static Func< int , int > GetAFunc() { var myVar = 1; Func< int , int > inc = delegate ( int var1) { myVar = myVar + 1; return var1 + myVar; }; return inc; } |
Hmmm, stare at that for just a second. When we call "GetAFunc", we get a method back that increments a local variable inside of the method. You see? "myVar" is a local variable, but when we return the "inc" method, it is bound inside of the delegate.
But don’t local variables get created on the stack? Don’t they go away when we finish executing the method? Normally yes. But if we ran this code, this would be the result:
So, when we passed back the method, the variable now lives along with the method. Outside of its original scope. You see, it got incremented when we called the method twice. Crazy!
But you know what is even more crazy? You just learned what a closure is! You just bound some free variables in the lexical environment! Don’t you just feel super smart now?
You see, it is oh so simple. It is really all about variables getting referenced which might leave scope. So you can say that these delegates are "closed" over these variables, which causes them to live outside of their lexical scope.
How Does It Work?
Well, the next question I always ask is, how does it work. And normally when people ask me this question I say "very carefully", but that is only because I am annoying. In reality this is implemented in a more straightforward manner than you might think. What we are really doing here is binding up a method with some data and passing it around. Geez, I sure do wish we had something like that in C#… oh wait, we do, it is called a class!
You see, the C# compiler detects when a delegate forms a closure which is passed out of the current scope and it promotes the delegate, and the associated local variables into a compiler generated class. This way, it simply needs a bit of compiler trickery to pass around an instance of the compiler generated class, so each time we invoke the delegate we are actually calling the method on this class. Once we are no longer holding a reference to this delegate, the class can be garbage collected and it all works exactly as it is supposed to!
Easy Peasy
Now, the C# compiler team would probably spit in my face for simplifying it this much, but at least at the conceptual level it is a fairly straightforward process. My head hurts a little bit thinking of all of the complex interactions and edge cases that would accompany something like this though.
Article From:http://www.codethinked.com/c-closures-explained
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
2010-08-10 GridView单元格合并 【转】
2010-08-10 利用ASP.NET AJAX的Timer讓GridView每隔一段時間做到自動換頁的功能
2010-08-10 利用JQuery一步步打造无缝滚动新闻
2010-08-10 JQuery中text()、html()和val()的区别