Edit
Attach
Printable
topic end
<!-- * Set TOPICTITLE = Claus Brod: How C# anonymous delegates implement closures (26 Feb 2006) --> <style type="text/css"> pre {background-color:#ffeecc;} </style> %STARTINCLUDE% <a name="26"></a> ---+++ [[BlogOnSoftware20060226][Delegating closures to C#]] (26 Feb 2006) [[BlogOnSoftware200602#23][Last time]], I looked at how closures work in Lisp, and tried to mimick them in C++ (without success) using function objects. To recap, a closure can be thought of as: * A function pointer referring to the code to be executed * A set of references to frames on the heap, namely references to all bindings of any free variables which occur in the code of the function. C# 2.0 introduces anonymous delegates. Their implementation actually goes beyond simple delegates; they are, in fact, closures (I think). Here is an example: <pre> class TestDelegate { public delegate void MyDelegate(); public MyDelegate GetDelegate() { string s = "Hiya"; return delegate() { Console.WriteLine(s); }; // anon delegate } static void Main(string[] args) { TestDelegate p = new TestDelegate(); MyDelegate anonDel = p.GetDelegate(); anonDel(); } } </pre> In the anonymous delegate, =s= is a free variable; the code compiles because the delegate refers to the definition of =s= in the surrounding code. If you run the above code, it will indeed print "Hiya", even though we are calling the delegate from =Main=, i.e. after we have left =GetDelegate()= which assigns that string to a _local_ variable. This is quite cool, considering that the .NET CLR uses a conventional stack and probably wasn't designed to run Lisp or Scheme all day. How do they do this? Let's look at the disassembled code of =GetDelegate()= (using .NET Reflector, of course): <verbatim> public TestDelegate.MyDelegate GetDelegate() { TestDelegate.<>c__DisplayClass1 class1 = new TestDelegate.<>c__DisplayClass1(); class1.s = "Hiya"; return new TestDelegate.MyDelegate(class1.<GetDelegate>b__0); } </verbatim> So the compiler morphed our code while we were looking the other way! Instead of assigning "Hiya" to a local variable, the code instantiates a funky =<>c__DisplayClass1= object, and that object apparently has a member called =s= which holds the string. The =<>c__DisplayClass1= class also has an equivalent of the original =GetDelegate= function, as it seems. Hmmm.... very puzzling - let's look at the definition of that proxy class now: <verbatim> [CompilerGenerated] private sealed class <>c__DisplayClass1 { // Methods public <>c__DisplayClass1(); public void <GetDelegate>b__0(); // Fields public string s; } public void <GetDelegate>b__0() { Console.WriteLine(this.s); } </verbatim> Aha, now we're getting somewhere. The compiler moved the code in the anonymous delegate to the function =<>c__DisplayClass1::<GetDelegate>b__0=. This function has access to the field =s=, and that field is initialized by the compiler when the proxy object is instantiated. So when the C# compiler encounters an anonymous delegate, it creates a proxy object which holds all "bindings" (in Lisp terminology) of free variables in the code of the delegate. That object is kept on the heap and can therefore outlive the original =GetDelegate()=, and that is why we can call the delegate from =Main= and still print the expected string instead of referring to where no pointer has gone before. I find this quite a cool stunt; I'm impressed by how the designers of C# are adding useful abstractions to the language. Lisp isn't the only language which supports closures, and maybe wasn't even the first, but I'm pretty sure that the folks at Microsoft were probably influenced by either Lisp (or Scheme) while developing anonymous delegates. It is amazing how such an old language continues to inspire other languages to this day. And that is, after reading a couple of good books and enlightening articles, what I understood about closures. Now, as a long-time boneheaded C++ programmer, I might have gotten it all wrong, and this blog entry is actually one way to test my assumptions; if my views are blatantly misleading, then hopefully somebody will point this out. (Well, if anybody reads this at all, of course.) What a simple and amazing concept those closures really are! I only had to shed all my preconceptions about the supposedly one and only way to call and execute functions and how to keep their parameters and variables on a stack... Closures are definitely very handy in all situations where callbacks are registered. Also, I already alluded to the fact that you could possibly build an object concept on top of closures in Lisp. And doesn't "snapshot of a function in execution" sound frighteningly close to [[http://en.wikipedia.org/wiki/Continuation]["continuation"]] or "coroutines"? (Answer: Yes, kind of, but not quite. But that's a different story.) I'm still trying to learn what closures do and how to best apply them in practice. But that doesn't mean they are constructs for the ivory tower: Knowing about them helped me only recently to diagnose and explain what originally looked like a memory leak in some Lisp test code that we had written. The final word of the jury is still out, but this is probably not a real leak, rather a closure which holds on to the binding of a variable, so that the garbage collector cannot simply free the resources associated with that variable. %COMMENT{type="below" nonotify="on"}% --- %STOPINCLUDE%
to top
End of topic
Skip to action links
|
Back to top
Edit
|
Attach image or document
|
Printable version
|
Raw text
|
Refresh
|
More topic actions
Revisions: | r1.5 |
>
|
r1.4
|
>
|
r1.3
|
Total page history
|
Backlinks
You are here:
Blog
>
BlogOnSoftware20060226
r1.5 - 05 Nov 2006 - 17:44 -
ClausBrod
to top
Blog
This site
2017
:
12
-
11
-
10
2016
:
10
-
7
-
3
2015
:
11
-
10
-
9
-
4
-
1
2014
:
5
2013
:
9
-
8
-
7
-
6
-
5
2012
:
2
-
10
2011
:
1
-
8
-
9
-
10
-
12
2010
:
11
-
10
-
9
-
4
2009
:
11
-
9
-
8
-
7
-
6
-
5
-
4
-
3
2008
:
5
-
4
-
3
-
1
2007:
12
-
8
-
7
-
6
-
5
-
4
-
3
-
1
2006:
4
-
3
-
2
-
1
2005:
12
-
6
-
5
-
4
2004:
12
-
11
-
10
C++
CoCreate Modeling
COM & .NET
Java
Mac
Lisp
OpenSource
Scripting
Windows
Stuff
Changes
Index
Search
Maintenance
Impressum
Datenschutzerklärung
Home
Webs
Atari
Blog
Claus
CoCreateModeling
Klassentreffen
Main
Sandbox
Sommelier
TWiki
Xplm
Jump:
Copyright © 1999-2024 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki?
Send feedback