В этом вопросе, упоминая компилятор, я на самом деле ссылаюсь на компилятор Roslyn. Проблема возникает при использовании IntelliSense, который считается одним и тем же компилятором.
Для демонстрационных целей и полноты используются следующие классы (с использованием Visual Studio 2015 с С# 6.0 и .NET 4.6.1):
public class A
{
public IEnumerable<B> B { get; set; }
}
public class B
{
public IEnumerable<C> C { get; set; }
}
public class C { }
public class Helper<T> { }
Ниже приведен следующий метод расширения:
public static void FooBar<T1, T2>(
this Helper<IEnumerable<T1>> helper,
Expression<Func<T1, IEnumerable<T2>>> expression) { ... }
Компилятор способен вывести его при потреблении следующим образом:
Helper<IEnumerable<B>> helper = ...;
helper.FooBar(l => l.C); //T1 is B and T2 is C
Также смотрите этот перегруженный метод расширения:
public static void FooBar<T1, T2, T3>(
this Helper<T1> helper,
Expression<Func<T1, IEnumerable<T2>>> expression1,
Expression<Func<T2, IEnumerable<T3>>> expression2) { ... }
Компилятор НЕ может вывести T1
при вводе его следующим образом:
Helper<A> helper = ...;
helper.FooBar(l => l. //compiler/IntelliSense cannot infer that T1 is A
Этот пример скриншота будет описывать больше того, что я имею в виду, не имея возможности сделать вывод:
Также я получаю это сообщение об ошибке при наведении указателя мыши на метод расширения с помощью мыши (я заменил символы <
и >
[
и ]
соответственно, потому что StackOverflow не может форматировать их цитата):
Аргументы типа для метода FooBar [T1, T2] (этот помощник [IEnumerable [T1]], выражение [Func [T1, IEnumerable [T2]]]) не могут быть выведены от использования. Попробуйте явно указать аргументы типа.
Но когда он выполняется вручную, выполните следующие действия:
helper.FooBar(l => l.B, l => l.C); //compiler infers that T1 is A, T2 is B and T3 is C
компилятор счастлив.
Почему компилятор /IntelliSense (или функция автозаполнения Visual Studio) не вычисляет T1
и не хочет, чтобы я явно указывал аргументы типа при вводе?
Обратите внимание, что если я опускаю IEnumerable<>
всюду в своих примерах, компилятор может с радостью сделать вывод во время ввода.
Компилятор также счастлив после ввода вручную l => l.B
. Затем он знает T1
is A
, поэтому вы можете выразить последний аргумент с помощью IntelliSense.