Вывод метода не работает с группой методов - PullRequest
11 голосов
/ 13 октября 2011

Рассмотрим

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 - не последний ли синтаксический сахар для первого?

Ответы [ 2 ]

18 голосов
/ 13 октября 2011

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

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

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

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

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

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

8 голосов
/ 13 октября 2011

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

...