Время компиляции и время выполнения С#
Мне было интересно, почему некоторые кавычки на С# проверяются во время компиляции, тогда как в других случаях ответственность сбрасывается на CLR. Как и выше, оба неверны, но обрабатываются по-другому.
class Base { }
class Derived : Base { }
class Other { }
static void Main(string[] args)
{
Derived d = (Derived)new Base(); //Runtime InvalidCastException
Derived d = (Derived)new Other(); //Compile-time Cannot convert type...
}
При чтении "С# in depth" я нашел информацию по этой теме, где автор говорит:
"Если компилятор видит, что на самом деле это невозможно сделать для этого приведения, itll запускает ошибку компиляции, и если ее теоретически разрешено, но фактически неверно во время выполнения, CLR генерирует исключение".
"Теоретически" означает связь иерархии наследования (какое-то другое сходство между объектами?) или это внутренний бизнес компилятора?
Ответы
Ответ 1
- Upcasts можно проверить во время компиляции - система типов гарантирует, что листинг преуспеет.
- В процессе компиляции нисходящие объекты не могут (вообще) проверяться, поэтому они всегда проверяются во время выполнения.
- Не связанные типы не могут быть переданы друг другу.
Компилятор рассматривает только статические типы. Среда выполнения проверяет динамический (runtime) тип.
Глядя на ваши примеры:
Other x = new Other();
Derived d = (Derived)x;
Статический тип x
равен Other
. Это не связано с Derived
, поэтому при выполнении компиляции выполняется сбой.
Base x = new Base();
Derived d = (Derived)x;
Статический тип x
теперь Base
. Что-то типа Base
может иметь динамический тип Derived
, так что это будет downcast. В общем случае компилятор не может узнать из статического типа x
, если он имеет тип Base
, Derived
другого класса под Base
. Таким образом, решение о том, разрешено ли приведение, остается на время выполнения.
Ответ 2
Если ваша переменная имеет тип Base
, теоретически может быть построена конструктором Derived
, поэтому фактически является переменной типа Derived
. Во время компиляции компилятор не беспокоится о попытке выяснить, может ли быть в каждом конкретном случае такой понижающий (представляющий переменную типа Base
как объект типа Derived
).
Ваш образец прост - вы создаете новый класс и сразу бросаете его. Но что, если вы получите Base
откуда-то еще, например, какой-нибудь вызов метода? Компилятор просто не может "догадаться" о том, что ваш метод будет возвращать, и, следовательно, не бросать ошибку.
Когда вы используете Other
, компилятор видит, что нет возможности, чтобы Other
на самом деле Derived
и генерирует исключение.