4

I'm trying to learn more about how lambdas work in C# and I'm a bit confused by the following example.

I have a weighted average function:

public static double? WeightedAverage<T>(this IEnumerable<T> records, Func<T, double?> value, Func<T, double?> weight)
{
    // snip actual calculation code
    if (denominator != 0)
        return numerator / denominator;
    return null;
}

In one place, I use a helper function that has a return type of double:

static double GetQuantity(Record record)
{
    return record.Quantity * record.Factor;
}

And pass that as the weight parameter into the weighted average function like this:

return _records.WeightedAverage(r => r.Price, r => ReportHelpers.GetQuantity(r));

The above seems to work fine.

The part that is confusing to me: In a few places ReSharper has recommended that I convert some of my lambdas into method groups. Playing around to try and see how this works, I changed the call to:

return _records.WeightedAverage(r => r.Price, ReportHelpers.GetQuantity);

This results in an error, Expected a method with 'double?' GetQuantity(Record) signature

Can someone tell me exactly what is happening behind the scenes here? Why does the lambda expression not produce a similar error since the return of the GetQuantity function is double and not double?? What is the difference with how the method group vs lambda expressions are being evaluated?

Thanks!

jeroenh
  • 26,362
  • 10
  • 73
  • 104
Jim
  • 2,300
  • 1
  • 19
  • 43
  • 1
    I think this is a very valid question. I looked at the [relevant section of the C# specification](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/conversions#method-group-conversions), but could not find a reason why this is not allowed... I'm probably missing something. – jeroenh Oct 16 '20 at 18:03

1 Answers1

3

The lambda expression doesn't have the error because its return type is inferred to be double?, matching the delegate argument, and you are explicitly returning a double from your method call. It works for the same reason that this works:

static double? GetDoubleOrNull() => 0.0d;

A double is a valid value for a nullable double to have.

A method group conversion, however, has additional rules that it needs to satisfy and, because it's trying to match a delegate, one of them is delegate compatibility. In particular, there is a line that says:

An identity or implicit reference conversion exists from the return type of M to the return type of D.

There is no identity or implicit reference conversion between double and double?, so the method group conversion fails. There is an implicit nullable conversion, but that isn't applicable here.

Chris Hannon
  • 4,134
  • 1
  • 21
  • 26