C# Lambda... are you using them wrong?

in #programming9 years ago (edited)

Thanks to LINQ, most C# developers are now comfortable passing functions/lambda to other functions, this does not mean they are always doing it right though. Looking through loads of code it appears too many believe lambda is the only way to supply function arguments.

Hopefully by the end of this article you will realize there is another, often better solution in the form of method groups. Something that has existed in the language since C# 2.

I will come back to what a method group is later but for now just think "method group == function pointer" for want of a better analogy.

First a simple example to highlight what I am talking about:

// This is a simple predicate, a Func<MyThing, bool> 
public bool IsWanted(MyThing thing) 
{ 
    // Some rules to decide 
    return ... 
} 
 
// And now that common pattern you see in LINQ 
var wanted = things.Where(thing => IsWanted(thing)); 

The above all looks fine and works 100% as expected. So, what is the problem?

Simple really, IsWanted() is already in the correct shape to pass directly to Where(). By adding the lambda you are building a new closure and adding a layer of indirection that is just pure pointless overhead. What you are actually doing is this:

public bool WrapIsWanted(MyThing thing) 
{ 
    return IsWanted(thing); 
} 

You would never do this in your day to day code passing around values/references so don't do it with functions. It gains you nothing and just adds a pointless level of indirection. The correct way to use our predicate in this:

var wanted = things.Where(IsWanted); 

Far cleaner, less noise and nicer to read I am sure you agree. So how do you spot when you can do this?
Follow the types!

Look at the type signatures for the expected argument and also your function. If they match you can pass directly otherwise you reshape it with a lambda. You actually do this all the time with normal values and don't give it a second thought.

Below we reshape intValue to a string so we can pass to function() which takes a string parameter.

function(intValue.ToString()); 

In reality, most of the time it's very easy to spot when you can use a method group. The following examples scream out for method group usage. They take in argument(s) and simply pass them onto function without any change in order etc.

You should be able to see the pattern:

x => function(x) 
(x, y) => function(x, y) 

The following examples require the lambda wrapper, the function they call have different signature to the lambda

x => x.func() 
(x, y) => function(y, x) // reversed 
(x, y) => x.function(y) 
(x, y) => y.function(x) 

You should be able to easily spot the difference between the two sets of examples.

When writing a predicate or map like function I tend to think about the use case to ensure I have a shape that directly plugs into all the places I might want to use it.

For LINQ this is actually easy, there are very few "function" shapes in LINQ. Here are some common operations and the types they expect:

public static IEnumerable<TR> Select<T, TR>( 
    this IEnumerable<T> source, Func<T, TR> mapper); 
 
public static IEnumerable<TR> Select<T, TR>( 
    this IEnumerable<T> source, Func<T, int, TR> mapper); 
 
public static IEnumerable<TR> SelectMany<T, TR>( 
    this IEnumerable<T> source, Func<T, IEnumerable<TR>> mapper); 
 
public static IEnumerable<TR> SelectMany<T1, T2, TR>( 
    this IEnumerable<T1> source, Func<T1, IEnumerable<T2>> mapper, 
    Func<T1, T2, TR> projection); 
 
public static IEnumerable<T> Where( 
    this IEnumerable<T> source, Func<T, bool> predicate); 

So, the following signatures cover well over 90+% of the use cases for LINQ.

Func<T, TR> 
 
Func<T, IEnumerable<TR>> 
 
Func<T, bool> 

When you have functions that take multiple arguments you can use "pseudo" partial application to ensure they are usable. This is a great sleight of hand :)

// Little chance of direct use :( 
public bool BadHasWantedName(MyThing thing, string name) 
{ 
    return thing.Name == name; 
} 
 
// Makes easy to reuse 
public Func<MyThing, bool> GoodHasWantedName(string name) 
{ 
    return thing => thing.Name == name; 
} 
 
// Think about this, it should mentally click given 
// what you have just read 
var wanted = things.Where(GoodHasWantedName("foobar")); 

In GoodHasWantedName() we pass in the name and it gives us a predicate for MyThing. It can be thought of as a function factory. This is a very common technique in functional programming languages.

This is a deep subject that requires you to rethink some ways you work but the payoff is cleaner code with less pointless abstractions. Take a scan you’re your code and see if you spot places this applies, I am sure there will be a few 🙂

Now, back to method group as promised earlier. To start with some of the C# language spec:

“Similar to the implicit anonymous method conversions described in §13.5, an implicit conversion exists from a method group (§14.1) to a compatible delegate type. If D is a delegate type, and E is an expression that is classified as a method group, then D is compatible with E if and only if E contains at least one method that is applicable in its normal form (§14.4.2.1) to any argument list (§14.4.1) having types and modifiers matching the parameter types and modifiers of blah, blah, blah, blah, blah"
I need a pint of coffee just to read through that and not fall asleep, so here is the TLDR:

A method group is all the functions in the same scope with the same name but different signature, a set of overloaded functions. This can contain one or more functions. There is also an implicit way to convert these functions to delegates based on the type signature so that the correct one is selected when possible.

var result = IsWanted(thing); 
var delegate = IsWanted; // Not called, just the name 

As always feel free to ask any questions you might have.

Happy coding

Woz

Sort:  

I do not like being a programmer, but this information helps me a lot! Thank you!

I have other stuff up you might find helpful, look through my posts.

How long you been coding?

Keep it up! This is quality content :)

oh... just the syntax sugar..

Not really. Take the following

var wanted = things.Where(thing => IsWanted(thing)); 
var wanted = things.Where(IsWanted); 

With a collection 1000 elements long the first one will result in 1000 extra function calls because of the pointless wrapper. So more stack manipulation etc.

So just using the method group saves a lot of work and is cleaner.

When you get to using partial application in C# then you are right, it does become about sugar, C# does not curry at a language level.

Just thought, there is also a good side effect of function factories to build closures vs lambda defined inline where used. It gives you explicit control over what is captured by the closure.

Are you a resharper user? Great tool but one thing it does it let you know when the C# compiler pulls in far more to closures than wanted. This actually happens a lot in reality and sometimes the only way to control is re-order the function code.

By using a function to scope and build a closure you gain explicit control over the process by limiting what is in scope in the first place :)

Here is an example of over capture

The 3 dots under the arrow of the lambda is resharper in action. Here it is warning me that the compiler is actually pulling in objects that it makes no use of. So that effects their object lifetime etc when it should not.

Coin Marketplace

STEEM 0.04
TRX 0.32
JST 0.081
BTC 61426.98
ETH 1631.00
USDT 1.00
SBD 0.42