Компилятор С# не распознает методы возврата доходности как похожие?

Если у меня есть два метода 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

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

Примечание. Я прочитал это и по-прежнему считаю, что первый должен скомпилировано нормально.

РЕДАКТИРОВАТЬ: Удалено использование var в фрагментах кода, поскольку я не собирался спрашивать об этом.


person mherle    schedule 22.07.2011    source источник
comment
См. также stackoverflow.com/q/6015747/38206.   -  person Brian Rasmussen    schedule 22.07.2011
comment
Вот почему я люблю программировать. = Д   -  person J. Steen    schedule 22.07.2011
comment
Требование редактировать хорошо демонстрирует недостаток с var   -  person Jodrell    schedule 22.07.2011
comment
@Дж. Стин: Из-за запутанных крайних случаев?   -  person BlueRaja - Danny Pflughoeft    schedule 22.07.2011
comment
@BlueRaja, нет, чтобы действительно узнать, что язык и компилятор делают, а что нет.   -  person J. Steen    schedule 25.07.2011


Ответы (8)



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

  • Func<int, IEnumerable<int>>
  • Func<int, IEnumerable>
  • Func<int, object>
  • любое количество настраиваемых типов делегатов

Компилятор не будет пытаться угадать, какой совместимый тип делегата вы ожидаете. Вы должны быть явными и указать - с приведением в вашем примере - какой именно тип делегата вы хотите использовать.

person LukeH    schedule 22.07.2011
comment
Почему тогда это не работает: (Func<int, IEnumerable<int>>)(1 == 0 ? OddNumbers : EvenNumbers)? - person Andrey; 22.07.2011
comment
@Andrey, метод не имеет встроенного типа, он есть только у делегатов. Вот почему тернарный оператор не может вывести возвращаемый тип. - person J. Steen; 22.07.2011

ну даже

var gen = OddNumbers;

не работает. Таким образом, вы не можете ожидать, что тернарный оператор будет работать.

Я думаю, var не может вывести тип делегата.

person Kugel    schedule 22.07.2011
comment
Func<int, IEnumerable<int>> generator = EvenNumbers; работает. Я не понимаю, почему тернарный оператор не должен работать. - person leppie; 22.07.2011
comment
Смотрите мой теперь удаленный ответ о том, что я ожидал. Ничего общего с var, это тернарный оператор. - person leppie; 22.07.2011
comment
Я согласен, что если не использовать тернарный оператор var, то это нужно понимать. Но это не так. - person Kugel; 22.07.2011
comment
@leppie: вывод делегата используется только тогда, когда вы напрямую назначаете группу методов типу делегата. Когда нет прямого присваивания, компилятор использует стандартный процесс, пытаясь сначала оценить правую часть изолированно, а затем определить, совместима ли она с левой. В этом случае условный оператор rhs не может определить, какой тип использовать, и терпит неудачу. - person LukeH; 22.07.2011
comment
@LukeH: с точки зрения разработчика компилятора, я не понимаю, почему это сложно сделать. Объем анализа, входящего в ?:, уже обеспечивает совместимость обоих типов возвращаемых данных. - person leppie; 22.07.2011
comment
@leppie, но у метода даже нет типа, поэтому у двух методов не может быть совместимых типов: их нет! - person Falanwe; 22.07.2011

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

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

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

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

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

Я не мог сказать наверняка.

person Jodrell    schedule 22.07.2011
comment
Я действительно думаю, что он хочет, чтобы генератор был делегатом, а не целым числом. - person Kugel; 22.07.2011
comment
да. Я хотел, чтобы одна из функций была назначена переменной generator. Я продолжу и отредактирую вопрос, чтобы люди не концентрировались на части var. - person mherle; 22.07.2011

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

person Hans Passant    schedule 22.07.2011
comment
@Jodrell: существует неявное преобразование из группы методов в совместимый тип делегата, но в этом конкретном примере проблема возникает из-за условного оператора. Тип условного выражения должен быть оценен первым, и это то, что невозможно сделать, потому что не существует неявного преобразования между двумя группами методов, поскольку группы методов не имеют типа. - person LukeH; 22.07.2011
comment
Эта ссылка содержит полезное описание группы методов stackoverflow.com/questions/886822/what-is-a-method-group-in-c/ - person Jodrell; 22.07.2011

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

Не работает:

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, последний тоже должен был дать сбой.

Я предполагаю, что проблема с тернарным оператором.

person Community    schedule 22.07.2011

Проблема в том, что утверждение

var gen = OddNumbers;

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

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

а также

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

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

person Henning Krause    schedule 22.07.2011
comment
Неа. Это тернарный оператор терпит неудачу. - person leppie; 22.07.2011
comment
Однако в тернарном операторе нет настоящей ошибки. Метод не имеет встроенного типа, он есть только у делегатов. Вот почему тернарный оператор не может вывести возвращаемый тип. - person J. Steen; 22.07.2011
comment
@Дж. Стин: замена var на предполагаемый тип тоже не работает. Это удивительно. - person leppie; 22.07.2011
comment
@leppie, я предполагаю, потому что тернарный оператор не может использовать левую часть для вывода типа. THAT, однако, вполне может быть какой-то логической ошибкой или неверным предположением, поскольку тернарный оператор — это просто синтаксический сахар. ;) - person J. Steen; 22.07.2011
comment
Компилятор не может вывести тип левой стороны (ИМХО из-за ответа, который я дал выше) и, следовательно, терпит неудачу. - person Henning Krause; 22.07.2011
comment
@леппи, @Дж. Стин: Вывод делегата используется только тогда, когда вы напрямую назначаете группу методов типу делегата (например, Func<int, IEnumerable<int>> gen = OddNumbers;). Когда вы напрямую не назначаете группу методов, вывод делегата не происходит, компилятор сначала пытается вычислить условный оператор и терпит неудачу. - person LukeH; 22.07.2011
comment
@ Хеннинг Краузе: Тогда Func<int, IEnumerable<int>> gen = OddNumbers; тоже потерпит неудачу. Чего нет. - person leppie; 22.07.2011
comment
@LukeH, ах! Конечно! Сначала вычисляем условный оператор. Имеет смысл. - person J. Steen; 22.07.2011
comment
@LukeH: Это имеет смысл, но есть ли у вас ссылка на это в спецификации? - person leppie; 22.07.2011
comment
@leppie: Просто копаюсь в спецификации, пытаясь найти ее. Пока не повезло, но я знаю, что он где-то там... - person LukeH; 22.07.2011
comment
@LukeH: Спасибо :) Я не помню, чтобы читал это в спецификации C# 2.0, но опять же, описания групп методов представляют собой собственный лабиринт :) - person leppie; 22.07.2011
comment
@leppie: я подозреваю, что все это связано с неявными преобразованиями. Несмотря на то, что группы методов (и анонимные функции) не имеют типа, они неявно преобразуются в любой совместимый тип делегата, поэтому Func<int, IEnumerable<int>> f = OddNumbers; или Func<int, IEnumerable<int>> g = x => new[] { x }; оба подходят. - person LukeH; 22.07.2011
comment
Но при тестировании, есть ли неявное преобразование между условным выражением и типом делегата, вам нужно сначала оценить тип условного выражения, и правила для вычисления условных выражений не сработают: не будет ни того, ни другого неявное преобразование из M1 в M2, но не из M2 в M1 или неявное преобразование из M2 в M1, но не из M1 в M2. - person LukeH; 22.07.2011

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

person J. Steen    schedule 22.07.2011