Неоднозначный вызов метода C # с делегатами

В моем приложении есть код, подобный следующему:

class Program
    {
        static void Main(string[] args)
        {
            Method(uri => Task.FromResult(uri));
        }

        static void Method(Func<Uri, Uri> transformer)
        {
            throw new NotImplementedException();
        }

        static void Method(Func<Uri, Task<Uri>> transformer)
        {
            throw new NotImplementedException();
        }
    }

Как и ожидалось, запуск этого кода вызывает вторую перегрузку Method, ожидающую делегата функции, возвращающего задачу. Однако, если я изменю код, чтобы избежать использования анонимного метода в Main:

class Program
    {
        static void Main(string[] args)
        {
            Method(Method2);
        }

        static Task<Uri> Method2(Uri uri)
        {
            return Task.FromResult(uri);
        }

        static void Method(Func<Uri, Uri> transformer)
        {
            throw new NotImplementedException();
        }

        static void Method(Func<Uri, Task<Uri>> transformer)
        {
            throw new NotImplementedException();
        }
    }

Компилятор C # теперь жалуется на то, что мой вызов метода "Method" неоднозначен. Что мне не хватает?


person richzilla    schedule 27.08.2015    source источник
comment
вы не передаете Uri в Method2.   -  person Sam Axe    schedule 27.08.2015
comment
@SamAxe, он не хочет. Он пытается преобразовать группу методов (Method2) в делегата (Func<Uri, Task<Uri>>)   -  person dcastro    schedule 27.08.2015
comment
Возможно, потому что TResult в Func<in T, out TResult> ковариантен.   -  person haim770    schedule 27.08.2015
comment
как предлагает Решарпер. бросить Method((Func<Uri, Task<Uri>>) Method2);   -  person M.kazem Akhgary    schedule 27.08.2015
comment
@ M.kazemAkhgary, ReSharper предлагают это для устранения неоднозначности. Вопрос в том, почему вообще существует двусмысленность, поскольку явно не существует неявного преобразования из Func<Uri, Uri> в Method2.   -  person haim770    schedule 27.08.2015
comment
@ haim770 это из-за типа возврата Task<Uri>. кажется, что компилятор даже не обращает на это внимания, чтобы разрешить эту двусмысленность. или, может быть, Task ‹Uri› сам возвращает Uri?   -  person M.kazem Akhgary    schedule 27.08.2015
comment
@richzilla Это действительно обман, и ответ здесь: принцип здесь заключается в том, что для определения конвертируемости группы методов требуется выбрать метод из группы методов с использованием разрешения перегрузки, а разрешение перегрузки не учитывает возвращаемые типы .   -  person dcastro    schedule 27.08.2015


Ответы (1)


Подробный ответ находится на https://stackoverflow.com/a/2058854/1223597 (как указал Richzilla).

Короткий ответ заключается в том, что команда компилятора C # решила, что преобразования групп методов (например, Method(Method2)) игнорируют возвращаемый тип (здесь Method2). Это дает им гибкость в том, как анализировать Expression деревья. К сожалению, это означает, что компилятор не может неявно выбирать между вашими 2 Method подписями.

При выполнении лямбда-преобразования (Method(uri => Task.FromResult(uri))) команде компиляторов не нужно беспокоиться о синтаксическом анализе дерева выражений, поэтому они действительно рассматривают возвращаемые типы.

person Joel V. Earnest-DeYoung    schedule 27.08.2015