2015-12-12 2 views
3

Я понимаю, что базовый класс не может быть преобразован в производный класс. Я не понимаю, почему это не поймано во время компиляции? Например:Ошибка передачи, не обнаруженная во время компиляции

class GradeBook 
{ 

} 

class ProfessorGradeBook : GradeBook 
{ 

} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook(); 
     //shouldn't this be a compile time error? 
    } 
} 

Я посмотрел на другие вопросы по StackOverflow, но она по-прежнему не имеет смысла для меня, почему это будет компилировать? ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook(); никогда не удастся ни при каких обстоятельствах (правильно?), Так почему же это ошибка во время выполнения вместо ошибки времени компиляции?

EDIT:

Я уже знал, почему компилятор никогда бы не поймать этот:

GradeBook a = new ProfessorGradeBook(); 
ProfessorGradeBook b = (ProfessorGradeBook)a; 

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

ProfessorGradeBook a = (ProfessorGradeBook)new GradeBook(); 

Я предполагаю, что ответ, который имеет наибольший смысл в первый комментарий Эрик Липперта, в частности, «подавляющее большинство разработчиков никогда бы не ввести эту строку кода» поэтому команда компилятора никогда не была заинтересована в попытке сделать ошибку.

+0

Это не проблема компиляции, так как компилятор отделить 2 команды. Сначала он создает новый экземпляр GradeBook, затем он пытается отличить объект от базового объекта, и это тоже хорошо. Компилятор не «знает», что этот объект является просто базовым объектом. –

+1

Вкратце: команда компилятора могла потратить время и энергию на создание предупреждения или ошибки. Цель потратить эти ограниченные усилия состоит в том, чтобы ** решить реальные проблемы, которые реально существуют у реальных разработчиков **. Явная не-цель: ** найти во время компиляции любую возможную ошибку **. Компилятор может легко предупредить об этом. Это происходит не потому, что подавляющее большинство разработчиков просто никогда не набирают это в первую очередь. Предупреждение, которое никогда не срабатывает, потому что никто не набирает этот код впустую, что может быть потрачено на разработку более полезных предупреждений. –

+0

Более конкретно, оператор литья имеет здесь очень специфическое значение; это означает, что «компилятор внимания», я пишу код, который вы не можете доказать, здесь является типичным. Я знаю, что он является типичным, доверяйте мне и разрешайте компиляции этого кода. Если я ошибаюсь, пожалуйста, провалите мою программу * «То есть, cast * явно отключает систему безопасности *. Компилятор вряд ли может быть обвинен в том, что не дал ошибку при выключении системы обнаружения ошибок! Когда вы пишете актерский состав *, вы несете ответственность за то, чтобы приведение было успешным, а не компилятор! –

ответ

0

Вы правы, что это никогда не преуспеет на практике, но инструкция new является инструкцией во время выполнения, а не командой времени компиляции, и вы делаете явное приведение к (ProfessorGradeBook), которое в основном говорит компилятор: «Эй, компилятор, просто поверь мне, это сработает».

И поэтому компилятор делает.

Есть сценарии, в которых можно было бы использовать такие вещи, как Fody или PostSharp добавить операторы преобразования после компиляции

0

компилятор не ловит все статически познаваемые ошибки. Это сталкивается с проблемой остановки. Компилятор поймает четко определенное подмножество всех статически обнаруживаемых ошибок.

Также обратите внимание, что компилятор не может быть произвольным. Спецификация языка C# говорит, насколько разумно это должно быть так, что все компиляторы C# ведут себя одинаково.

Как вам это нравится? Хочешь, чтобы это поймало это?

static void Main(string[] args) 
{ 
    var g = new GradeBook(); 
    ProfessorGradeBook a = (ProfessorGradeBook)g; 
    //shouldn't this be a compile time error? 
} 

Это труднее. Мы можем сделать это произвольно.

1

Это downcast. Компилятор не может знать, может ли менее типизированная ссылка быть литой более специализированным типом.

Например:

public class A {} 
public class B {} 

A a = new B(); 
B b = (B)a; 

a набирается, как A в то время как объект, хранящийся там типа B.Кстати, когда вы пытаетесь бросить его B, так как экземпляр A может быть A сам или любой производный класс от A (включая B, но не только B), компилятор не может предположить, что a не будет литьевой - B, потому что вы утверждаете, что это возможно с использованием явного литья.

В конце дня введите метаданные. Если вы объявите ссылку как A, вы сообщаете компилятору, что все, что вы там установили, будет A, предполагая, что вы теряете метаданные времени компиляции для доступа к более специализированным членам из возможного производного типа. Другими словами: вы сообщаете компилятору, что ссылка A, и вы не заботитесь о метаданных производного типа во время компиляции, и любой downcast будет оцениваться во время выполнения, потому что компилятор не может доказать понижение, если только код выполняется, и среда выполнения обнаруживает, что так называемый downcast невозможен , поскольку явно предоставленный тип не является частью иерархии исходного типа.

Вероятно, компиляторы могут быть в состоянии поймать недопустимые downcasts, но это может также увеличить время сборки ...

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