Why Do Some C# Lambda Expressions Compile to Static Methods

Why do some C# lambda expressions compile to static methods?

This is most likely because there are no closures, for example:

int age = 25;
Action<string> withClosure = s => Console.WriteLine("My name is {0} and I am {1} years old", s, age);
Action<string> withoutClosure = s => Console.WriteLine("My name is {0}", s);
Console.WriteLine(withClosure.Method.IsStatic);
Console.WriteLine(withoutClosure.Method.IsStatic);

This will output false for withClosure and true for withoutClosure.

When you use a lambda expression, the compiler creates a little class to contain your method, this would compile to something like the following (the actual implementation most likely varies slightly):

private class <Main>b__0
{
public int age;
public void withClosure(string s)
{
Console.WriteLine("My name is {0} and I am {1} years old", s, age)
}
}

private static class <Main>b__1
{
public static void withoutClosure(string s)
{
Console.WriteLine("My name is {0}", s)
}
}

public static void Main()
{
var b__0 = new <Main>b__0();
b__0.age = 25;
Action<string> withClosure = b__0.withClosure;
Action<string> withoutClosure = <Main>b__1.withoutClosure;
Console.WriteLine(withClosure.Method.IsStatic);
Console.WriteLine(withoutClosure.Method.IsStatic);
}

You can see the resulting Action<string> instances actually point to methods on these generated classes.

Does the C# compiler treat a lambda expression as a public or private method?

It depends. With the current version of Visual Studio, the methods that implement lambdas are never public, but they're not always private. A simple program to test some versions of lambdas:

public class Program
{
public static void Main()
{
var program = new Program();
Try("A", program.A);
Try("B", program.B);
Try("C", program.C);
Console.ReadKey();
}

private static void Try(string name, Func<Action> generator)
{
var mi = generator().Method;
Console.WriteLine($"{name}: DeclaringType={mi.DeclaringType}, Attributes={mi.Attributes}");
}

private Action A() => () => { };
private Action B() => () => { ToString(); };
private Action C()
{
var c = 1;
return () => c.ToString();
}
}

prints

A: DeclaringType=Scratch.Program+<>c, Attributes=PrivateScope, Assembly, HideBySig
B: DeclaringType=Scratch.Program, Attributes=PrivateScope, Private, HideBySig
C: DeclaringType=Scratch.Program+<>c__DisplayClass4_0, Attributes=PrivateScope, Assembly, HideBySig

A's lambda doesn't have any captures. It's created as an internal method of an empty closure class.

B's lambda captures this. It's created as a private method of the containing class.

C's lambda captures c. It's created as an internal method of a non-empty closure class.

All of this is undocumented and has changed in the past, so it would be good to avoid relying on it. What matters is that when you call the anonymous method, it behaves as specified. If you need anything more than that, you shouldn't be using anonymous methods. Depending on what you're after, you might either still be able to use lambdas, but with expression trees, or you might need to create regular named methods instead.

How to create a static lambda for use with expression building?

In case others wish to know, in the end, I had to promote (demote? lol) my expressions to "Expression-bodied function members" instead, like this:

// (class method)
static string _Convert(object obj) => (obj as SomeType)?.SomeProperty ?? ReturnSomethingElse;

then in my method body:

Func<object, string> conversionDelegate = _Convert;
Expression exp = Expression.Convert(expression, typeof(SomeType), conversionDelegate.Method);

Edit: Some talk about non-capturing/static lambdas here: https://github.com/dotnet/csharplang/issues/275

Why has a lambda with no capture changed from a static in C# 5 to an instance method in C# 6?

I don't have an answer as to why that is so (reproduced locally, too).

However, the answer to:

Why is it so? How can this be avoided so that Expression.Call would
begin to work again in new Visual Studio?

You can do this (works on both compilers):

Action<int, int> a = (x, y) => Console.WriteLine(x + y);

ParameterExpression p1 = Expression.Parameter(typeof(int), "p1");
ParameterExpression p2 = Expression.Parameter(typeof(int), "p2");

MethodCallExpression call;
if (a.Method.IsStatic)
{
call = Expression.Call(a.Method, p1, p2);
}
else
{
call = Expression.Call(Expression.Constant(a.Target), a.Method, p1, p2);
}

Thanks to Jeppe Stig Nielsen for fix regarding a.Target

Why is Action.Method.IsStatic different between Visual Studio 2013 and 2015 for certain lambda expressions

Check the answer in the following link :
Delegate caching behavior changes in Roslyn

Basically what changed and i quote @Yuzal from the linked answer :

"Delegate caching behavior was changed in Roslyn. Previously, as
stated, any lambda expression which didn't capture variables was
compiled into a static method at the call site. Roslyn changed this
behavior. Now, any lambda, which captures variables or not, is
transformed into a display class:"

And by display class he meant a generated private sealed class within which encapsulates the instance method invoked by the action delegate.

Why the change was made ? Quoting @Kevin Pilch-Bisson (a member of the C# IDE team) :

The reason it's faster is because delegate invokes are optimized for
instance methods and have space on the stack for them. To call a
static method they have to shift parameters around.

So basically the comment is self explanatory. The behaviour difference you see in the example above is because they noticed that if the Action delegate invoked instance methods its faster than invoking static methods regardless if the lambda captures variables or not.

Why compiled lambda build over Expression.Call is slightly slower than delegate that should do the same?

The compiled expression may be slower because of the reasons:

TL;DR;

The question is, why is the compiled delegate way slower than a manually-written delegate? Expression.Compile creates a DynamicMethod and associates it with an anonymous assembly to run it in a sand-boxed environment. This makes it safe for a dynamic method to be emitted and executed by partially trusted code but adds some run-time overhead.

There are tools like FastExpressionCompiler which help to mitigate the problem (disclaimer: I am an author)

Update: View IL of compiled delegate

  1. It is possible to get the compiled delegate IL as byte array:

    var hello = "Hello";
    Expression<Func<string>> getGreetingExpr = () => hello + " me";

    var getGreeting = getGreetingExpr.Compile();

    var methodBody = getGreeting.Method.GetMethodBody();

    var ilBytes = methodBody.GetILAsByteArray();
  2. You need a way to parse/read the array and convert it into IL instructions and parameters.

Pity, but I did not find the tooling or robust NuGet package to allow me to do so :-(

Here is the related SO question.

The closest tool may be this.

Why Local Functions generate IL different from Anonymous Methods and Lambda Expressions?

Anonymous methods stored in delegates may be called by any code, even code written in different languages, compiled years before C# 7 came out, and the CIL generated by the compiler needs to be valid for all possible uses. This means in your case, at the CIL level, the method must take no parameters.

Local methods can only be called by the same C# project (from the containing method, to be more specific), so the same compiler that compiles the method will also be handled to compile all calls to it. Such compatibility concerns as for anonymous methods therefore don't exist. Any CIL that produces the same effects will work here, so it makes sense to go for what's most efficient. In this case, the re-write by the compiler to enable the use of a value type instead of a reference type prevents unnecessary allocations.

Don't understand compile error for method call with lambda expression

The problem is that f => f.A cannot be used to infer the type of f.

So, easy fix:

Run<Foo, int>(f => f.A, 15);

Notice that if you have to specify one of the type arguments, you need to specify all of them.

Your other method works because T is inferred from the first parameter T obj

Difference in CSC and Roslyn compiler's static lambda expression evaluation?

This was a deliberate change made by the Roslyn team.

Delegates that point to instance methods are slightly faster to invoke, so Roslyn now compiles lambdas to instance methods even when it doesn't need to.

See discussion.

Are C# Lambda Expressions Type Safe and when (complile time/runtime) are they checked?

In C# exist two types of Lambda Expression:

A lambda expression is an anonymous function that you can use to create delegates or expression tree types.

The fist type of lambda expression is synctatic sugar for an anonymous function:

Func<int, int> myFunc = x => x + 1;

is totally equivalent to:

Func<int, int> myFunc = delegate(int x) { return x + 1; };

so it is clearly type safe, because it is C# code with a different makeup.

The other type of Lambda Expression is the one that generates Expression Trees:

Expression<Func<int, int>> myFunc = x => x + 1;

This is something different. This isn't compiled to "code" but is compiled to some object of type Expression that "describe" the x => x + 1 (and even describe the type of delegate)... it is compiled to:

ParameterExpression par = Expression.Parameter(typeof(int), "x");
Expression<Func<int, int>> myFunc2 = Expression.Lambda<Func<int, int>>(
Expression.Add(par, Expression.Constant(1)),
par);

Now, this code can't be executed directly. It can be converted to executable code through the .Compile() method. In general a .Compile()d expression tree is type safe, but Expression Trees aren't normally made to be simply compiled. Programs tend to manipulate them to obtain funny result. They can be used for various tasks... For example to extract the "name" of properties or "methods" without including in the code a string with the name of the property or method, or to be converted to other languages (Entity Framework/LinqToSQL convert expression trees to SQL). An Expression Tree is quite safe (it is possible to "manually build" at runtime an invalid expression, but when you do the .Compile() you'll get an exception, and expression trees accepted by the C# compiler are normally safe to be compiled), but if the expression tree is used for other things, then errors could occur, even errors connected to type safety.

I'll quote from: Why the anonymous type instance cannot accept null values returned by the entity framework query?

var l =  (from s in db.Samples
let action = db.Actions.Where(x => s.SampleID == x.SampleID && x.ActionTypeID == 1).FirstOrDefault()
where s.SampleID == sampleID
select new
{
SampleID = s.SampleID,
SampleDate = action.ActionDate,
}).ToList();

Equivalent more or less to

var l = db.Samples.Select(s => new
{
s = s,
action = db.Actions.Where(x => s.SampleID == x.SampleID && x.ActionTypeID == 1).FirstOrDefault()
}).Where(x => x.s.SampleID == sampleID).Select(x => new
{
SampleID = x.s.SampleID,
SampleDate = x.action.ActionDate
}).ToList();

Here ActionDate is DateTime, and so is SampleDate. This LINQ expression will be transformed by the compiler to a big Lambda Expression, and executed by Entity Framework SQL Server-side. Now... the problem is that action could become null, and so action.ActionDate could be null (because the expression won't be executed locally there won't be a NullReferenceException), and an exception could be thrown (will be thrown) when null is put in SampleDate (an InvalidCastException I think). So while the expression is type-safe, what the library does with it causes the expression to generate non-type-safe code (an invalid cast)



Related Topics



Leave a reply



Submit