Подтвердить что ты не робот

Время компиляции и время выполнения С#

Мне было интересно, почему некоторые кавычки на С# проверяются во время компиляции, тогда как в других случаях ответственность сбрасывается на 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 генерирует исключение".

"Теоретически" означает связь иерархии наследования (какое-то другое сходство между объектами?) или это внутренний бизнес компилятора?

4b9b3361

Ответ 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 и генерирует исключение.