2012-01-05 4 views
3
class Sample 
{ 
    public static T M<T, TParam1>(TParam1 param1) 
    { 
     return default(T); 
    } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 

     double d = Sample.M((int)121); 
    } 
} 

Этот код не компилируется и приводит следующее сообщение об ошибке:Тип вывода не работает

аргументы типа для метода «ThreadPoolTest.Sample.M (TParam1)» не может быть выведено из использование. Попробуйте указать аргументы типа явно

Почему в этом примере не работает вывод типа?

+0

Вы имели в виду вывод? – slaphappy

+1

@kbok Я думаю, что это вмешательство в этот момент: D – user1231231412

+0

Я исправил заголовок. – tomfanning

ответ

9

Тип вывода может использовать аргументы для вызова метода. Тот факт, что вы назначаете результат double, совершенно не имеет отношения к вопросу о типе. Другими словами, насколько компилятор обеспокоен, он должен выяснить, что это означает:

Sample.M((int) 121) 

без дополнительной информации. Например, вы могли бы означать Sample.M<int, int>, или Sample.M<double, int>, или Sample.M<string, int> - нет никакой информации, так скажите, какая из них лучше подходит.

В списке параметров вы не указываете T, поэтому вывод типа не может помочь.

+0

Спасибо за объяснение! Печально, что C# не понимает, что я имею в виду именно тип переменной (двойной в этом случае). Я думаю, что в большинстве случаев очевидно, что именно ожидаемый тип выражения означает. В случае, когда требуется специальный тип, очень просто указать явно следующее: double d = (string) Sample.M ((int) 121); Желательно хотя бы частичное назначение типа: double d = Sample.M ((int) 121); – Win4ster

0

Поскольку вы не указали тип возврата.

Вот то, что вы хотите: ответ

double d = Sample.M<double, int>((int)121); 
+0

Фактически явный (int) листинг является избыточным. – Aphelion

+0

Вы правы, так как тип TParam1 явно указан. – ken2k

+0

Даже если 'TParam1' не были указаны, буква' 121' является 'int'. – phoog

7

Джона, конечно, правильный. Поучительно подумать, почему C# не рассматривает «возвращаемый тип» при выполнении выводов. Основной принцип здесь является то, что информация о типе перетекает из внутри к вне, а не из вне к внутри при анализе выражения.

В вашем конкретном случае совершенно очевидно, что тип предполагаемого типа возврата является тем, что вы назначаете нечто, что однозначно удваивается. Но есть много ситуаций, когда это совершенно не очевидно:

static R M<A, R>(A a) { return default(R); } 
static void N(int x, double y) {} 
static void N(double x, int y) {} 
... 
N(M(123), 456); 

ОК, что теперь? Если информация о типе «то, что назначается», должна указывать в на вывод типа на M, тогда информация о типе, которая должна протекать, «это может быть int или double».

Но подождите, это правильно? Мы можем сделать более логичные выводы здесь о том, каков должен быть тип возвращаемого типа.

Если это int, то у нас есть N(M<int, int>(123), 456), а разрешение перегрузки на N будет терпеть неудачу, потому что этот вызов неоднозначен; это может быть либо версия N!

Следовательно, это не может быть int, не так ли? Он должен быть двойным, потому что тогда это означает, что вызов N(M<int, double>(123), 456), который однозначно является второй перегрузкой.

Теперь представьте себе, что для вызова формы Q (R (S (N (M (...с дюжиной перегрузок каждого, и, возможно, с несколькими лямбдами, брошенными туда. Анализ становится безумно сложным; для пользователей становится сложно правильно и сложно понять, что делает их программа, почему она создает ошибки и как их исправлять.

Гораздо проще просто сказать, что анализ типов выражения должен определяться из его содержимого, а не его контекста. Вот что мы делаем. Проблема разрешения перегрузки, которую вы бросаете в компилятор, должна быть разрешима, не рассматривая контекст проблемы; мы рассматриваем только содержимое списка аргументов для решения проблемы, и в списке аргументов недостаточно информации.

Теперь исключение составляют лямбда. Типы параметров Lambda - это, выведенные из их контекста, и в результате вы можете легко заставить компилятор попробовать триллионы возможных назначений типов для глубоко вложенных лямбда, чтобы решить проблему разрешения перегрузки. Но мы не хотим распространять трудности анализа лямбда на весь язык; мы хотим ограничить эти трудности конкретными языковыми особенностями, которые в них нуждаются.

+1

Что делает визуальная студия-идеолог, если вы слишком случайно попадаете в ситуацию, которая вынуждает триллион оценок? Есть ли какой-то тайм-аут, чтобы остановить блокировку машины/ядра? Я не знаю, как создать образец кода, чтобы продемонстрировать его, или я просто попробую сам. – asawyer

+0

@asawyer: Попробуйте это. Предположим, что у вас есть типы X и Y, методы A (действие ), A (действие (Y)), B (действие ), B (действие ), C ... и так далее, n из них и метод M, который принимает n аргументов. Предположим, что n равно 3, а M - M (X, Y, X). Постройте вызов A (a => B (b => C (c => M (a, b, c))). Как разрешение перегрузки разрешает вызовы A, B, C и M? Оно должно проверять все возможные комбинация «a is X или Y, b - X или Y, c - X или Y», чтобы определить, что X, Y, X является наилучшим совпадением *. Это 2 из 3 возможных комбинаций. сделайте так, чтобы «3» было сколь угодно большим, гнездясь глубже. –

+0

@asawyer: и, конечно, если у вас есть перегрузки Action , Action , Action , Action , тогда вы получите 4 возможных n возможных комбинаций. Если у вас есть m перегрузки и n nestings, тогда компилятор должен попробовать m для n возможных оценок для n лямбда-параметров. Если m и n - десять, то это десять миллиардов комбинаций прямо там. Это делает Visual Studio * крайне недовольным *. –

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