What Are C# Lambda's Compiled Into? a Stackframe, an Instance of an Anonymous Type, Or

What are C# lambda's compiled into? A stackframe, an instance of an anonymous type, or?

Assuming you mean "as a delegate", then it still depends :p if it captures any variables (including "this", which may be implicit) then those variables are actually implemented as fields on a compiler-generated type (not exposed anywhere public), and the statement body becomes a method on that capture class. If there are multiple levels of capture, the outer capture is again a field on the inner capture class. But essentially:

int i = ...
Func<int,int> func = x => 2*x*i;

Is like;

var capture = new SecretType();
capture.i = ...
Func<int,int> func = capture.SecretMethod;

Where:

class SecretType {
public int i;
public int SecretMethod(int x) { return 2*x*i; }
}

This is identical to "anonymous methods", but with different syntax.

Note that methods that do not capture state may be implemented as static methods without a capture class.

Expression trees, on the other hand... Are trickier to explain :p

But (I don't have a compiler to hand, so bear with me):

int i = ...
Expression<Func<int,int>> func = x => 2*x*i;

Is something like:

var capture = new SecretType();
capture.i = ...
var p = Expression.Parameter("x", typeof(int));
Expression<Func<int,int>> func = Expression.Lambda<Func<int,int>>(
Expression.Multiply(
Expression.Multiply(Expression.Constant(2),p),
Expression.PropertyOrField(Expression.Constant(capture), "i")
), p);

(except using the non-existent "memberof" construct, since the compiler can cheat)

Expression trees are complex, but can be deconstructed and inspected - for example to translate into TSQL.

Do c# lambdas exist only at compile time?

Yes. In addition to a delegate, lambdas become a generated method. The delegate refers to that method. If they close over variables, the method becomes an instance method on a generated class holding the closure state.

In that sense you can use a lambda and local variables to create a class with fields and one method, similar to JavaScript.

Is it possible to actually see the expression of lambda in Locals or Watch windows in Visual Studio?

You can't directly see the contents of a delegate. They are designed to not know anything about what they themselves will do upon calling Invoke.

Action a = () => Console.WriteLine(); // getting "Console.WriteLine()" from "a" is not possible.

But the debugger can help you figure out what the lambda is.

You can write expressions in the "Immediate" window to evaluate them.

Func<int, int> negate = x => -x; // let's say you can't see this line but you know "negate" exists.
// in Immediate window, you can write the following
// negate.Invoke(1) // -1
// negate.Invoke(-1) // 1
// then you can guess that "negate" will negate the number, though not conclusively

However, I think the best way is to trace back your code to see where that delegate came from. This way, you can find the original method/lambda that was added to the delegate.

You mentioned that the delegate is passed to a method as a parameter. This means that you can look down the stack trace and see which method called that method. The delegate might be created there. If not, look down the stack again until you can find it.

If the delegate is stored in a field/property, then you have to search for when that field/property is set, which can be hard.

EDIT:

See this post: What are C# lambda's compiled into? A stackframe, an instance of an anonymous type, or?

Lambdas are compiled into, essentially, methods of some type. Can you "see" the implementation of a method in the debugger? No.

Function Parameters Across Different Assemblies

The lambda is compiled into B and executed there.

You can read up more on how it works here: What are C# lambda's compiled into? A stackframe, an instance of an anonymous type, or?

Why can't c# use inline anonymous lambdas or delegates?

Lambdas in C# do not have types, until they are used in a context that casts them to a delegate or Expression type.

That's why you cannot do the following:

var x = () => "some lambda";

You might enjoy Eric Lippert's Series on Lambda Expressions vs Anonymous Methods

  • Lambda vs Anon - Part One
  • Lambda vs Anon - Part Two
  • Lambda vs Anon - Part Three
  • Lambda vs Anon - Part Four
  • Lambda vs Anon - Part Five

Inline helper methods in c#

The problem is that you understand lambdas in c# incorrectly. When compiler translate c# to MSIL lambdas become classes and so you have nothing to inline. You can take a look at great Marc Gravell post on SO. So, whether or not you define lambdas in external class, you'll need to get an object from heap (i simplify compiled code behaviour). And so, as i think, there'll be no difference in performance for your application.

How are anonymous functions implemented in AOT-compiled languages?

int y = 3;
auto f = [y](int x) { return x*y; };

this is a C++11 lambda. The complier (basically) converts it to:

struct __anonymous_name__ {
int operator()(int x) const { return x*y; }

int y;
};
__anonymous_name__ f = {y};

where everything with __ in the name is not actually named, just given names for exposition purposes.

At runtime, everything has a fixed type, no code is generated.

std::function<int(int)> can store a copy of f above, but that uses a type erasure mechanism that goes beyond the scope of this question. Note, however, that f is not an object of type related to std::function<int(int)>; C++ has more than one kind of polymorphism.


I also seriously doubt that Java/C# lambdas are JIT'd any more than the rest of your code.

How to replace a huge lambda expression with a private method adding a parameter?

private static Action<AuthorizationPolicyBuilder> ConfigBuilderFactory(string param)
{
return builder => builder
.RequireClaim("special_claim", param)
.RequireClaim("claim1", "value1")
...;
}
options.AddPolicy("ExtensivePolicy_A", ConfigBuilderFactory("a"));
options.AddPolicy("ExtensivePolicy_Z", ConfigBuilderFactory("z"));

How to work around not being able to use refs in a lambda?

When using the ref keyword, the type must also be given. Like this:

(ref object data, ref SDL_Event e) => { ... }

The parameter list of a lambda is like the parameter list of an ordinary named method. The types can be left out in a lambda, however, but only when no parameters have modifyers such as ref, out, params.

How is a struct on the heap deallocated?

When a local variable is captured in the lambda, it becomes a regular field in the compiler-generated lambda class. For example, this:

static void Main(string[] args) {
int capture = 5;
Func<int, int> func = input => input * capture;
}

Can be lowered by a C# compiler to this:

[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
public int capture;
internal int <Main>b__0(int input)
{
return input * this.capture;
}
}
private static void Main(string[] args)
{
new C.<>c__DisplayClass0_0().capture = 5;
}

Thus:

Then, how/when is it deallocated?

It is deallocated just like a regular value type field of a class instance, when the class instance is deallocated.

Is the value boxed (then it must work like any other reference)?

In general, we use boxing to refer to the implicit representation-changing conversion of a value type to a special reference type whose only function is to (later) enable non-representation changing assignments of the original value to reference type locations (typically of type object or any interface type that the original value type implemented). Under the hood, boxing is performed by the CLR using a special IL instruction.

In the context of lambdas, even though the value probably goes to the heap as a field of a class instance, we don't call it boxing in the regular sense. Under the hood, capturing a variable is something that the C# compiler takes care of, by writing some equivalent boilerplate code for you.

Of course, as pointed out in the comments, in the end they both boil down to the same thing: they allocate a struct on the heap.

Is there a special functionality in the GC to manage structs on the heap?

Nothing special is required for lambda captures specifically. The GC already knows how to deallocate value type fields.



Related Topics



Leave a reply



Submit