Ответ 1
UPDATE: этот вопрос был тема моего блога 22 октября 2012 года. Спасибо за отличный вопрос!
Почему компилятор не может определить тип типа
M(dynamic_expression)
типа компиляции, если есть только одна перегрузка M или все перегрузки M имеют одинаковый тип возврата?
Компилятор может определить тип времени компиляции; тип времени компиляции является динамическим, и компилятор успешно показывает это.
Я думаю, что заданный вами вопрос:
Почему тип
M(dynamic_expression)
времени компиляции всегда динамичен, даже в редком и маловероятном случае, когда вы делаете совершенно ненужный динамический вызов методу M, который всегда будет выбран независимо от типа аргумента?
Когда вы формулируете такой вопрос, он сам отвечает.: -)
Причина одна:
Случаи, которые вы предполагаете, редки; для того, чтобы компилятор мог сделать вид вывода, который вы описываете, достаточно знать информацию, чтобы компилятор мог выполнить почти полный статический анализ этого выражения. Но если вы в этом сценарии, то почему вы используете динамику в первую очередь? Вы бы гораздо лучше сказали:
object d = whatever;
Foo foo = new Foo();
int x = (d is string) ? foo.M((string)d) : foo((int)d);
Очевидно, что если есть только одна перегрузка M, то это еще проще: нарисуйте объект на нужный тип. Если это не удастся во время выполнения, потому что это плохое, ну, динамика тоже потерпела бы неудачу!
В этих сценариях просто нет необходимости в динамической динамике, поэтому зачем нам делать много дорогостоящих и сложных операций вывода типа в компиляторе, чтобы включить сценарий, который мы не хотим использовать динамическим для первое место?
Причина вторая:
Предположим, мы действительно сказали, что разрешение перегрузки имеет очень специальные правила, если группа методов статически известна, что она содержит один метод. Отлично. Теперь мы просто добавили новый тип хрупкости в язык. Теперь добавление новой перегрузки изменяет тип возвращаемого вызова на совершенно другой тип - тип, который не только вызывает динамическую семантику, но также и типы значений. Но подождите, все становится хуже!
// Foo corporation:
class B
{
}
// Bar corporation:
class D : B
{
public int M(int x) { return x; }
}
// Baz corporation:
dynamic dyn = whatever;
D d = new D();
var q = d.M(dyn);
Предположим, что мы реализуем вашу функцию requiest и делаем вывод, что q является int, по вашей логике. Теперь корпорация Foo добавляет:
class B
{
public string M(string x) { return x; }
}
И вдруг, когда корпорация Baz перекомпилирует свой код, внезапно тип q спокойно переходит в динамический, потому что во время компиляции мы не знаем, что dyn не является строкой. Это странное и неожиданное изменение в статическом анализе! Почему третье лицо, добавляющее новый метод в базовый класс, вызывает изменение типа локальной переменной в совершенно другом методе в совершенно другом классе, написанном в другой компании, компании, которая даже не использует B напрямую, но только через D?
Это новая форма проблемы с хрупким базовым классом, и мы стремимся минимизировать проблемы с хрупким базовым классом в С#.
Или, что, если вместо этого Foo corp сказал:
class B
{
protected string M(string x) { return x; }
}
Теперь, по вашей логике,
var q = d.M(dyn);
дает q тип int, когда код выше находится за пределами типа, который наследуется от D, но
var q = this.M(dyn);
дает тип q как динамический, если внутри типа, который наследуется от D! Как разработчик, я бы нашел это совершенно неожиданным.
Причина три:
Слишком много умений на С#. Наша цель - не создавать логический движок, который мог бы выработать все возможные ограничения типа для всех возможных значений, заданных конкретной программой. Мы предпочитаем иметь общие, понятные, понятные правила, которые можно легко записать и реализовать без ошибок. Спецификация уже составляет восемьсот страниц, и писать компилятор без ошибок невероятно сложно. Пусть не усложнится. Не говоря уже о расходах на тестирование всех этих сумасшедших случаев.
Причина четыре:
Кроме того: язык предоставляет вам много возможностей для использования анализатора статического типа. Если вы используете динамический, вы , в частности, просим, чтобы этот анализатор отложил свое действие до времени выполнения. Не следует удивляться, что использование функции "перестать делать статический тип во время компиляции" приводит к тому, что анализ статического типа не очень хорошо работает во время компиляции.