C # необязательный параметр на func, теряющий вывод обобщенного типа - PullRequest
0 голосов
/ 01 июня 2018

У меня есть тривиальный обобщенный enum метод синтаксического анализа:

public static T Parse<T>(string value) => (T)Parse(typeof(T), value, false);

и использование:

    public IEnumerable<DocumentTypesEnum> FileTypeEnums => 
        FileTypes.Split(',').Select(Extensions.StringEnumerator.Parse<DocumentTypesEnum>);

Выше компилируется нормально.

Добавление необязательного параметра в Parse<T>:

public static T Parse<T>(string value, bool ignoreCase = false) => 
        (T)Parse(typeof(T), value, ignoreCase);

Теперь компилятор недоволен!

enter image description here

Он работает просто отлично, предоставляя аргумент / тип явно (это то, что сообщение об ошибке говорит в любом случае :)):

public IEnumerable<DocumentTypesEnum> FileTypeEnums => 
            FileTypes.Split(',').Select(f => Extensions.StringEnumerator.Parse<DocumentTypesEnum>(f));

Но мне любопытно, почему компилятор жалуется, просто добавив необязательный параметр в селектор func?

Примечание: Ближайшее обсуждение, которое я могу найти: Невозможно определить универсальный тип с необязательными параметрами , но это больше из именованных аргументов выдайте необязательные аргументы, как указано Джон Скит .

Ответы [ 3 ]

0 голосов
/ 01 июня 2018

Давайте упростим это, чтобы убрать вывод типа и разрешение перегрузки с картинки.Вам нужно преобразование группы методов , которое учитывает необязательные параметры - и это просто не является частью правил языка C #.

Вот более простой конкретный пример:

using System;

class Program
{
    static void Foo(int x = 1)
    {
        Console.WriteLine(x);
    }

    static void Main()
    {
        Action action = Foo; // Error
        action();
    }
}

Не удается скомпилировать, потому что нет метода Foo без параметров (с void типом возврата).

Спецификация языка могла бы быть написана так, чтобыКомпилятор сгенерирует метод, который только что вызвал Foo с необязательным параметром, а затем создаст делегат, ссылающийся на этот новый метод, но он не написан таким образом.Я полагаю, что в тот момент, когда вы создаете делегат из преобразования группы методов, список вызовов этого делегата всегда является самим соответствующим методом, а не своего рода «прокси-методом».

Соответствующая частьСтандарт ECMA C # 5 - это раздел 11.8, который содержит:

Рассмотренные методы-кандидаты - это только те методы, которые применимы в их обычной форме и не пропускают никаких дополнительных параметров (§12.6.4.2) * * тысяча двадцать-один.Таким образом, подходящие методы игнорируются, если они применимы только в расширенной форме, или если один или несколько их необязательных параметров не имеют соответствующего параметра в D .

0 голосов
/ 01 июня 2018

Ответ Малахии объясняет, почему это происходит.

Если вы хотите, чтобы способ был правильно адресован, ответ состоит в том, чтобы просто иметь два метода Parse - один с двумя параметрами и один с одним, а второй вызвать первый.

public static T Parse<T>(string value, bool ignoreCase) 
    => (T)Parse(typeof(T), value, ignoreCase);

public static T Parse<T>(string value) => Parse<T>(value, false);
0 голосов
/ 01 июня 2018

Это потому, что даже если у вас есть необязательный параметр, разрешение компилятора не имеет значения - подпись параметра должна совпадать при вызове вашего Select().

Входной параметр выбора больше или меньше Func<TInput, TOutput> с TInput, равным string и TOutput, равным DocumentTypesEnum.В этом случае нет места для другого параметра.

Когда вы делаете явный вызов, вы фактически создаете новый delegate с приемлемой подписью, который затем вызывает вашу «неприемлемую» подпись со скрытой /необязательный параметр.

...