.NET: предполагаемые универсальные типы для статических методов - PullRequest
14 голосов
/ 21 января 2010

Предположим, у меня есть

public static List<T2> Map<T,T2>(List<T> inputs, Func<T, T2> f)
{
    return inputs.ConvertAll((x) => f(x));
}

private int Square(int x) { return x*x; }

public void Run()
{
    var inputs = new List<Int32>(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<Int32,Int32>(inputs, Square);

    // this is also fine (thanks, Jason)
    var outputs2 = Map<Int32,Int32>(inputs, (x)=>x*x);

    // also fine
    var outputs2 = Map(inputs, (x)=>x*x);
}

Почему он не компилируется?

РЕДАКТИРОВАТЬ : Ошибка:

ошибка CS0411: Аргументы типа для метода Namespace.Map (System.Collections.Generic.List , System.Func ) 'не могут быть выведены из использования. Попробуйте указать аргументы типа явно.

Почему я должен указывать тип функции Map ()? Не может ли это сделать вывод из пройденного Func<T>? (в моем случае квадрат)


Является ли ответ такой же, как для
Вывод универсального типа C # 3.0 - передача делегата в качестве параметра функции ?

Ответы [ 5 ]

10 голосов
/ 21 января 2010

Из сообщения об ошибке:

Аргументы типа для метода '[...].Map<T,T2>(System.Collections.Generic.List<T>, System.Func<T,T2>)' не могут быть выведены из использования. Попробуйте указать аргументы типа явно.

Обратите внимание, что в сообщении об ошибке говорится, что оно не может определить аргументы типа. То есть он не может разрешить один из параметров типа T или T2. Это из-за §25.6.4 (Вывод аргументов типа) спецификации. Это часть спецификации, которая касается выводов параметров универсального типа.

Ничто не выводится из аргумента (но вывод типа завершается успешно), если выполняется любое из следующих условий:

[...]

Аргумент является группой методов.

Таким образом, компилятор не может использовать тип делегата Square для вывода типа 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 из-за того, что inputs является List<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?
    }
}
2 голосов
/ 21 января 2010

Сбой вывода при выводе типа делегата, а не списка:

// 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);

Я не знаю почему, хотя - может быть, просто нет неявного преобразования типов из подписи от Square до Func<Int32, Int32>? Кажется странным, что Func<int, int> t = Square; является действительным, но компилятор не может сделать скачок сам по себе ... Ошибка, может быть?

1 голос
/ 21 января 2010

Причина, по которой это не работает, заключается в том, что 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
1 голос
/ 21 января 2010

После небольшой копки я обнаружил, что ваше подозрение относительно другого ответа верное. Вот что говорит спецификация C # 3.0:

7.4.2.1 - Для каждого из аргументов метода Ei: если Ei является анонимным функция или группа методов, явная вывод типа параметра (7.4.2.7) сделал... 7.4.2.7 - ... Если E является явно типизированной анонимной функцией с Типы параметров U1… Uk и T является тип делегата с типами параметров V1… Vk, то для каждого Ui точный логический вывод (§7.4.2.8) сделан из Ui для соответствующего Vi.

Другими словами, анонимные методы и группы методов (то есть Square) могут выводить только типы параметров явно . Я думаю, что обоснование в конце ответа, на который вы ссылались, подытоживает. Поскольку вывод типа не всегда может быть сделан неявно из группы методов, компилятор даже не пытается выполнить это, согласно спецификации.

0 голосов
/ 21 января 2010

Следующее также работает; Я не знаю почему:

var outputs = Map(inputs, i => Square(i));
...