Компилятор C# не распознает методы возврата yield как аналогичные?


Если у меня есть два метода yield return с одной и той же сигнатурой, компилятор, похоже, не распознает их сходство.

У меня есть два метода yield return, такие как:

    public static IEnumerable<int> OddNumbers(int N)
    {
        for (int i = 0; i < N; i++)
            if (i % 2 == 1) yield return i;
    }
    public static IEnumerable<int> EvenNumbers(int N)
    {
        for (int i = 0; i < N; i++)
            if (i % 2 == 0) yield return i;
    }

С этим я ожидал бы, что следующее утверждение будет компилироваться нормально:

Func<int, IEnumerable<int>> generator = 1 == 0 ? EvenNumbers : OddNumbers; // Does not compile

Я получаю сообщение об ошибке

Тип условного выражения не может быть определен, поскольку существует нет неявного преобразования между "группой методов" и "группой методов"

Однако, явное приведение работы:

Func<int, IEnumerable<int>> newGen = 1 == 0 ? (Func<int, IEnumerable<int>>)EvenNumbers : (Func<int, IEnumerable<int>>)OddNumbers; // Works fine

Я что-то пропустил или это ошибка в компиляторе C# (я использую VS2010SP1)?

Примечание: я прочиталэто и все еще считаю, что первый должен был быть составлен отлично.

EDIT: убрал использование var в фрагментах кода, поскольку это было не то, что я собирался спросить.

8 10

8 ответов:

Нет. Это не ошибка. Он не имеет ничего общего с yield. Дело в том, что тип выражения method group может быть преобразован в тип delegate только тогда, когда он присваивается непосредственно как: SomeDel d = SomeMeth.

Спецификация C# 3.0 :

§6.6 метод групповых преобразований

Неявное преобразование (§6.1) существует из группы методов (§7.1) в совместимый тип делегата.

Это единственное неявное преобразование, возможное с группами методов.

Как тернарный оператор оценивается в терминах типов умозаключений:

A ? B : C:

Убедитесь, что либо B, либо C могут быть неявно приведены к типу друг друга. Например, A ? 5 : 6.0 будет double, потому что 5 может быть неявно приведено к double. Тип A и B в этом случае является method group, и нет никакого преобразования между method group. Только делегировать, и его можно принудить, как вы это сделали.

Существует множество возможных типов делегатов, которые могут соответствовать сигнатуре методов EvenNumbers и OddNumbers. Например:

  • Func<int, IEnumerable<int>>
  • Func<int, IEnumerable>
  • Func<int, object>
  • любое количество пользовательских типов делегатов
Компилятор не будет пытаться угадать, какой тип совместимого делегата вы ожидаете. Вам нужно быть явным и сказать ему-с приведением в вашем примере-какой именно тип делегата вы хотите использовать.

Ну даже

var gen = OddNumbers;

Не работает. Поэтому вы не можете ожидать, что троичный оператор будет работать.

Я думаю, что var не может определить тип делегата.

yield Return не имеет к этому никакого отношения.

Вы не устанавливаете generator на IEnumerable<int>, вы устанавливаете его на MethodGroup, то есть функцию без скобок, чтобы сделать вызов.

Второе утверждение приводит MethodGroups к Delegates, которые можно сравнить.

Возможно, вы хотите сделать что-то вроде но,

var generator = 1 == 0 ? EvenNumbers(1) : OddNumbers(1);

Я не могу сказать точно.

Это не имеет ничего общего с итераторами, один и тот же код не компилируется, если методы являются простыми функциями. Компилятор неохотно автоматически преобразует метод в объект делегата, забывая использовать функцию () в вызове метода-слишком распространенная ошибка. Вы должны сделать это явно.

Свертка того, что работает и не работает:

Не работает:

var generator = 1 == 0 ? EvenNumbers : OddNumbers;
Func<int, IEnumerable<int>> generator = 1 == 0 ? EvenNumbers : OddNumbers;

Работает:

var generator = 1 == 0 ? (Func<int, IEnumerable<int>>)EvenNumbers : OddNumbers;

Если бы это было как-то связано с yield или var, последнее также должно было бы потерпеть неудачу.

Мое предположение-это проблема с тернарным оператором.

Проблема заключается в том, что утверждение

var gen = OddNumbers;

Можно интерпретировать как

Func<int, IEnumerable<int>> gen = OddNumbers;

И

Expression<Func<int, IEnumerable<int>> gen = OddNumbers;

Компилятор не может этого решить, поэтому вы должны сделать это.

Метод (группа методов)не имеет внутреннего типа, только делегаты. Именно поэтому тернарный оператор не может вывести тип для возврата, и поэтому вы должны привести одно или другое возвращаемое значение в качестве типа, который вы хотите вернуть.