.NET: Выведенные универсальные типы на статических методах

Предположим, что я имею

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 универсальный вывод типа - передача делегата как параметр функции?

15
задан Community 23 May 2017 в 12:23
поделиться

5 ответов

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

Аргементы типа для метода « [...]. Карта (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?
    }
}
10
ответ дан 1 December 2019 в 04:34
поделиться

Немного покопавшись, я обнаружил, что ваше подозрение насчет другого ответа верно. Вот что говорит спецификация 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 для соответствующего Вай.

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

1
ответ дан 1 December 2019 в 04:34
поделиться

Вывод не удается при выношении типа делегата, а не в списке:

// 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 T = Square; действителен, но компилятор не может сделать прыжок самостоятельно ... ошибка, может быть?

2
ответ дан 1 December 2019 в 04:34
поделиться

Причина, по которой это не работает, предназначена для 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
ответ дан 1 December 2019 в 04:34
поделиться

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

var outputs = Map(inputs, i => Square(i));
0
ответ дан 1 December 2019 в 04:34
поделиться