Предположим, что я имею
public static List Map(List inputs, Func f)
{
return inputs.ConvertAll((x) => f(x));
}
private int Square(int x) { return x*x; }
public void Run()
{
var inputs = new List(new int[]{2,4,8,16,32,64,128,256,512,1024,2048});
// this does not compile
var outputs = Map(inputs, Square);
// this is fine
var outputs2 = Map(inputs, Square);
// this is also fine (thanks, Jason)
var outputs2 = Map(inputs, (x)=>x*x);
// also fine
var outputs2 = Map(inputs, (x)=>x*x);
}
Почему это не компилирует?
Править: Ошибка:
ошибка CS0411: аргументы типа для метода 'Пространство имен. Карта
(Система. Наборы. Универсальный. Список , Система. Func )', не может быть выведен из использования. Попытайтесь указать аргументы типа явно.
Почему я должен указать тип Карты () функция? Разве это не может вывести это из переданного Func
? (в моем случае, Квадрате)
Ответ то же что касается
C# 3.0 универсальный вывод типа - передача делегата как параметр функции?
Из вашего сообщения об ошибке:
Аргементы типа для метода «
[...]. Карта
'не может быть выведен из использования. Попробуйте уточнить аргументы типа явно.(System.Collections.Generic.List , System.Func )
Обратите внимание, что сообщение об ошибке говорит, что он не может понять аргументы типа. То есть у него есть проблемы с разрешением одного из типовых параметров T
или T2
. Это из-за §25.6.4 (вывод аргументов типа) спецификации. Это часть спецификации сделки с параметрами вывода генерического типа.
Ничего не выводится из аргумента (но вывод типа добивается успеха), если какое-либо из следующих действий:
[...]
Аргумент - это группа метода.
Таким образом, компилятор не может использовать тип делегата квадрата
, чтобы вывести тип T2
. Обратите внимание, что если вы измените декларацию на
public static List<T> Map<T>(List<T> inputs, Func<T, T> f) {
return inputs.ConvertAll((x) => f(x));
}
, то
var outputs = Map(inputs, Square);
является законным. В этом случае он уже решил, что T
INT
от того факта из того факта, что входы
- это список
.
Теперь более глубокий вопрос - это то вышесказанное описание? То есть зачем не использовать группы методов роли в разрешении параметра типа? Я думаю, что это из-за таких случаев:
class Program {
public static T M<T>(Func<T, T> f) {
return default(T);
}
public static int F(int i) {
return i;
}
public static float F(float f) {
return f;
}
static void Main(string[] args) {
M(F); // which F am I?
}
}
Немного покопавшись, я обнаружил, что ваше подозрение насчет другого ответа верно. Вот что говорит спецификация C# 3.0:
7.4.2.1 - Для каждого из аргументов метода Ei: Если Ei анонимный функция или группа методов, явный вывод типа параметра (7.4.2.7) - это сделанный... 7.4.2.7 - ...если Е является явно типизированной анонимной функцией с помощью типы параметров U1...Uk и T - это тип делегата с типами параметров V1... Vk, затем для каждого Ui точный вывод (§7.4.2.8) сделан из Ui для соответствующего Вай.
Другими словами, анонимные методы и группы методов (каким является Квадрат), могут выводить только типы параметров явно . Я думаю, что обоснование в конце ответа, на которое вы ссылались, хорошо подводит итог. Поскольку вывод типа не всегда может быть сделан неявно из группы методов, компилятор даже не пытается его сделать, согласно спецификации.
Вывод не удается при выношении типа делегата, а не в списке:
// this is also fine
var outputs3 = Map(inputs, new Func<int, int>(Square));
// more calls that compile correctly
var outputs4 = Map(inputs, x => Square(x));
var outputs5 = Map(inputs, x => x * x);
Func<int, int> t = Square;
var outputs6 = Map(inputs, t);
Я не знаю, почему, хотя, возможно, нет неявных типов от подписи Квадрат
- FUNC
? Кажется странным, что FUNC
действителен, но компилятор не может сделать прыжок самостоятельно ... ошибка, может быть?
Причина, по которой это не работает, предназначена для C #, чтобы сделать вывод типа на метод, он должен знать тип делегата на другом конце конвертации. Но на данный момент тип целевого делегата до сих пор не полностью известен - только T (INT) известен, T2 все еще не решен.
Func<int, int> f = Square;
//works because we provided the destination type
//of the conversion from Square to delegate
Map(inputs, i => Square(i));
//works because the lambda follows the actual method call
//and determines its own return type
Следующее также работает; Я не знаю, почему:
var outputs = Map(inputs, i => Square(i));