Подтвердить что ты не робот

Вывод метода не работает с группой методов

Рассмотрим

void Main()
{
    var list = new[] {"1", "2", "3"};
    list.Sum(GetValue); //error CS0121
    list.Sum(s => GetValue(s)); //works !
}

double GetValue(string s)
{
    double val;
    double.TryParse(s, out val);
    return val;
}

Описание ошибки CS0121:

Вызов неоднозначен между следующими методами или свойствами: 'System.Linq.Enumerable.Sum<string>(System.Collections.Generic.IEnumerable<string>, System.Func<string,decimal>)' и 'System.Linq.Enumerable.Sum<string>(System.Collections.Generic.IEnumerable<string>, System.Func<string,decimal?>)

Что я не понимаю, какая информация s => GetValue(s) дает компилятору, что просто GetValue нет - не последний синтаксический сахар для первого?

4b9b3361

Ответ 1

Отметьте ответ правильно, но можете использовать немного больше объяснений.

Проблема действительно связана с тонкой разницей в том, как обрабатываются группы методов и как обрабатываются лямбды.

В частности, тонкая разница заключается в том, что группа методов считается конвертируемой в тип делегата исключительно на основе соответствия аргументов, а не также от того, будет ли return type. Lambdas проверяет оба аргумента и тип возврата.

Причиной этого нечетного правила является то, что преобразования групп методов в делегаты по сути являются решением проблемы разрешения перегрузки. Предположим, что D - тип делегата double D(string s), а M - группа методов, содержащая метод, который берет строку и возвращает строку. При разрешении значения преобразования из M в D мы делаем перегрузку, как если бы вы сказали M (строка). Разрешение перегрузки выбрало бы M, которое берет строку и возвращает строку, и поэтому M можно конвертировать в этот тип делегата, даже несмотря на то, что преобразование приведет к ошибке позже. Так же, как "регулярное" разрешение перегрузки будет успешным, если вы скажете "строка s = M (null)"; - разрешение перегрузки преуспевает, хотя это и приводит к ошибке преобразования позже.

Это правило тонкое и немного странное. Его результатом является то, что ваша группа методов конвертируется во все типы делегатов, которые являются вторыми аргументами каждой версии Sum, которая принимает делегат. Поскольку наилучшее преобразование не найдено, разрешение перегрузки в группе методов Sum неоднозначно.

Правила преобразования групп групп правдоподобны, но немного нечетны в С#. Меня несколько раздражает то, что они не согласуются с более "интуитивно правильными" преобразованиями лямбда.

Ответ 2

s => GetValue(s) - это лямбда-выражение, а GetValue - группа методов, что совершенно другое. Они могут считаться синтаксическим сахаром для new Func<string,double>(...), но единственный способ, которым они связаны друг с другом, состоит в том, что выражение лямбда включает вызов GetValue(). Когда дело доходит до преобразования в делегат, группы методов имеют разные правила преобразования, чем лямбда-выражения в отношении возвращаемых типов. См. Почему Func <T> неоднозначный с аргументом Func < IEnumerable <T> ? и Перегруженный аргумент группы методов смущает разрешение перегрузки?.