2013-12-18 5 views
9

я провел следующие тесты логического вывода:Почему C# не может вывести тип аргумента типа общего типа подписями статического метода не общего?

static class InferenceTest { 
    static void TakeInt(int a) { } 
    static int GiveInt() { return 0; } 
    static int TakeAndGiveInt(int a) { return 0; } 

    static void ConsumeAction1<T>(Action<T> a) { } 
    static void ConsumeFunc1<T>(Func<T> f) { } 
    static void ConsumeFunc2a<T1, T2>(Func<T1, T2> f) { } 
    static void ConsumeFunc2b<T>(Func<int, T> f) { } 
    static void ConsumeFunc2c<T>(Func<T, T> f) { } 
    static void ConsumeFunc1Func2<T1, T2>(Func<T1> f1, Func<T1, T2> f2) { } 

    static void Main() { 
     ConsumeAction1(TakeInt);  //error 
     ConsumeFunc1(GiveInt);   //ok 
     ConsumeFunc2a(TakeAndGiveInt); //error 
     ConsumeFunc2b(TakeAndGiveInt); //ok 
     ConsumeFunc2c(TakeAndGiveInt); //error 
     ConsumeFunc1Func2(GiveInt, TakeAndGiveInt); //ok 
    } 
} 

Результатов, кажется, предполагает, что C# компилятор не может вывести аргументы типа общих для параметров функции делегата от не-родовой группы методов.

Что меня озадачивает больше всего, что C# это может вывести аргументы типа для Func<T1, T2> из возвращаемых значений методов в ConsumeFunc1Func2, но не в состоянии сделать вывод типов для Func<T, T> в ConsumeFunc2c.

Этот вопрос похож на вопрос T of Func<S, T> is inferred from output of lambda expression only when S and T are different?, но вместо lambdas с неизвестными типами параметров у нас есть не общие группы методов.

Why can't C# infer type from this seemingly simple, obvious case вопрос типа ответов на вопросы «Почему недвусмысленные не общие методы недостаточно для вывода?» и «Почему существует разница между типами аргументов и типом возвращаемого значения для вывода?».

Вопросы:

Почему C# компилятор может определить тип Func<T> используя тип возвращаемого значения, но не видит успех в Func<T, T> случае?

Почему C# компилятор может вывести T1 аргумент типа для Func<T1, T2> из Func<T1> в ConsumeFunc1Func2, но не может вывести T аргумент типа для Func<T, T> от себя в ConsumeFunc2c, который, кажется, проще?

+0

В ConsumeFunc1Func2 компиляция по-прежнему выводит только из возвращаемого значения, а не типов параметров. T1 разрешен из возвращаемого значения GiveInt, а T2 разрешен из возвращаемого значения TakeAndGiveInt. Таким образом, никакая дополнительная тайна, добавленная случаем ConsumeFunc1Func2. – Baldrick

+1

Я бы неплохо прочитал раздел 7.5.2 спецификации C# 4.0. Это вполне читаемо и описывает различные этапы вывода типа и как они относятся к группам методов. – Baldrick

+0

'ConsumeFunc2b' показывает, что для' Func 'тип возврата' T' может быть разрешен из 'TakeAndGiveInt'. Но когда '?' Также является 'T', как в случае с' Func 'в' ConsumeFunc2c', компилятор, похоже, забыл, что параметр 'T' совпадает с уже выведенным' T '. В отличие от успеха ConsumeFunc1Func2. –

ответ

1

Параметры метода не проверяются.

Как указано, в ConsumeFunc1Func2 компилятор выводит только из возвращаемых значений. В ConsumeFunc2c подпись TakeAndGiveInt не проверяется, является ли ее тип параметра метода фактически одного типа метода return type cause ... параметры метода не проверяются!

1

В общем случае имя метода не будет однозначно идентифицировать уникальный тип Action<T>, которому может быть присвоена группа методов. Например, даже если существует только одна перегрузка Fred и требуется один аргумент Cat, эта перегрузка может быть назначена не только на Action<Cat>, но и на некоторые другие типы, такие как Action<Mammal>, Action<Animal> или Action<Object>. Хотя бывают случаи, когда одна замена типа во всех отношениях превосходит любую альтернативу, это не всегда так. Чище определить язык, требующий указания типа делегата, чем для того, чтобы компилятор попытался «угадать», тем более, что если угадать компилятор будет означать, что многие вещи, которые не должны нарушать изменения, будут (например, добавление перегрузки метода может сделать неоднозначный вывод типа, который использовался для работы).

Смежные вопросы