Принятый ответ - разумное объяснение, но я подумал, что могу предоставить немного больше подробностей.
Итак, допустим, вы используете FirstOrDefault в DbSet. DbContext.Foos.FirstOrDefault(x=> x.Bar == true);
Прежде всего, я надеюсь, что вы не написали бы это. Если вы хотите спросить "идет дождь?"ты спрашиваешь "идет дождь?"или вы спрашиваете "является ли утверждение, что идет дождь, истинное утверждение?"Просто скажите FirstOrDefault(x => x.Bar)
.
Далее, учитывая следующие перегрузки:
public static TSource FirstOrDefault<TSource>(
this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate)
public static TSource FirstOrDefault<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
Как компилятор выбирает, какая перегрузка является лучшей?
Сначала мы делаем введите логический вывод , чтобы определить, что TSource
есть в каждом. Детали алгоритма вывода типов сложны;задайте более сфокусированный вопрос, если у вас есть вопрос по этому поводу.
Если вывод типа не может определить тип для TSource
в любом из них, метод неудачного вывода отбрасывается из набора кандидатов. В вашем примере TSource
может быть определено как Foo
, предположительно.
Далее, из оставшихся кандидатов мы проверяем их на применимость аргументов к формальным формам . То есть можем ли мы преобразовать каждый предоставленный аргумент в соответствующий ему тип формального параметра? (И, конечно, правильное ли количество аргументов и т. Д.) В вашем примере применимы оба метода.
Из оставшихся применимых кандидатов теперь мы вводим раунд проверки правильности. Как работает проверка на лучшее? Опять же, мы делаем это аргумент за аргументом. В этом случае у нас есть два вопроса:
DbContext.Foos
можно преобразовать в IEnumerable<Foo>
или IQueryable<Foo>
. Что, если либо, является лучшим преобразованием? - Лямбда может быть преобразована либо в делегат, либо в дерево выражений. Что, если либо, является лучшим преобразованием?
На второй вопрос легко ответить: ни тот, ни другой лучше. Мы ничего не узнаем из этого аргумента в отношении лучшего поведения.
Чтобы ответить на первый вопрос, мы применяем правило преобразование в конкретное лучше, чем преобразование в общее . Если у вас есть возможность перейти на Жирафа или Млекопитающее, то лучше перейти на Жирафа. Итак, теперь вопрос в том, что является более конкретным , IQueryable<Foo>
или IEnumerable<Foo>
?
Правило проверки специфичности является простым: если X может быть неявно преобразовано в Y, но Y не может бытьнеявно преобразуется в X, тогда X является более конкретным. Жираф можно использовать там, где требуется животное, но нельзя использовать животное там, где нужен жираф, поэтому жираф более конкретен. Или: каждый жираф - это животное, но не каждое животное - это жираф, поэтому жираф более специфичен.
По этому показателю IQueryable<T>
более специфичен, чем IEnumerable<T>
, поскольку каждый запрашиваемый является перечислимым, но некаждое перечисляемое является запрашиваемым.
Таким образом, запрашиваемое является более конкретным, и, следовательно, это преобразование лучше.
Теперь мы задаем вопрос "существует ли уникальный применимый кандидатметод, в котором по сравнению с каждым другим кандидатом по крайней мере одно преобразование было лучше и ни одно преобразование не было хуже ? "Есть;запрашиваемый кандидат обладает тем свойством, что он лучше в одном аргументе, чем в любом другом, и не хуже в любом другом аргументе, и это единственный метод, обладающий этим свойством.
Следовательно, разрешение перегрузки выбирает этот метод.
Я рекомендую вам прочитать спецификацию, если у вас есть дополнительные вопросы.