2

У меня есть следующий метод:Почему компилятор не выводить общий тип

public static void Foo<T>(Predicate<T> validator) { ... } 

И я хочу, чтобы назвать это следующим образом:

Foo(s => string.IsNullOrEmpty(s)); 

Почему не компилятор выяснить, s есть string и поэтому T есть string? Какое правило в спецификации делает алгоритм вывода неудачным здесь?

+0

Обратите внимание, что вы можете создать делегат от 'string.IsNullOrEmpty', вам не нужно лямбда (а на самом деле лямбда только создает дополнительный слой, который замедляет работу до мелочей) –

+0

@BenVoigt Но разве это не обязывает меня явно указывать общий тип, который, очевидно, сделает работу типа вывода? 'Foo (string.IsNullOrEmpty)' или даже 'Foo (новый Predicate (string.IsNullOrEmpty))'. – InBetween

+0

@PrestonGuillot Да, редактировал мой комментарий, когда писал. Я просто задавался вопросом о причине этого. Я все еще думаю, что компилятор должен был это сделать правильно; нет никакой двусмысленности в отношении вызова, только один кандидат действителен, поэтому можно указать тип параметра lamba. Возможно, просто из-за последовательного поведения они решили не иметь причины компилятора, так как сразу не станет очевидным, почему иногда вывод или не будет работать. – InBetween

ответ

1

Я признаю, что я не проверил всю C# 5.0 спецификации, но раздел 8.5.1 декларация локальной переменных говорит немного о var ключевом слове, которое используется для объявления распознанного типа.

Вот правила для переменных, объявленных для var и для всех выводимых переменных:

  • Локальная переменная декларация не может включать в себя несколько локальных переменных при-declarators.
  • Локальная переменная-декларатор должна включать инициализатор локальной переменной.
  • Инициализатор с локальной переменной должен быть выражением.
  • Инициализатор выражение должно иметь тип времени компиляции.
  • Инициализатором выражение не может относиться к самой объявленной переменной

Поскольку это лямбда, ваш Inferred инициализатор:

string.IsNullOrEmpty(s) 

ОК, так что ...

  • У него нет нескольких локальных переменных-деклараторов. Проходить.
  • Он включает в себя local-variable-intitalizer, потому что это лямбда. Проходить.
  • Это выражение. Проходить.
  • string.IsNullOrEmpty возвращает строку. Проходить.
  • Он полагается на себя, передается функции для определения типа. Потерпеть неудачу.

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

Лямбда может исправить это относительно легко, хотя:

Foo(string s => string.IsNullOrEmpty(s)); 
+0

Спасибо! Итак, в основном причина заключается в том, что тип параметра лямбда не может быть выведен из формального типа параметра метода, потому что для разрешения вызова метода вам нужно знать сам тип параметра; вы в конечном итоге бегаете по кругу. Это имеет смысл, я думаю, хотя это * немного поразительно, что алгоритм должен потерпеть неудачу в таких очевидных случаях, но все же удастся сделать хорошую работу в гораздо более запутанных сценариях. Я знал, что решение было легко; явно специфицирует тип параметра lamda или сам общий тип; оба являются всего лишь одной строкой. Мне просто интересно, почему. – InBetween

+0

Я предполагаю, что я здесь, но это должен был быть сценарий, когда компилятор должен был понять это правильно; если кандидат, установленный в действительных методах, содержит только одного кандидата (как в этом случае), вывод должен работать. '' 'может быть только' string', нет никакой другой допустимой перегрузки, которая могла бы заставить компилятор не решить более подходящее совпадение. – InBetween