Оба оператора предполагают вызов конструктора по умолчанию Dog
, как вы упоминаете;следовательно, очевидно, что в обоих случаях создается экземпляр Dog
.Это означает, что оба оператора заканчиваются инициализацией переменной с идентичным экземпляром (это часть оператора после равенства).
Однако операторы также имеют другую часть: объявление переменной (это часть оператора до равенства).В статически типизированных языках, таких как C #, каждая переменная - в общем, любое выражение - имеет статический тип:
object a = new Dog(); // static type: object / runtime type: Dog
Dog b = new Dog(); // static type: Dog / runtime type: Dog
Компилятор не позволит вам присвоить значение переменной, которую он не может Докажи, что имеет статический тип переменной, например, он не позволяет
Cat c = new Dog(); // unless Dog derives from Cat, which we know isn't true
, поскольку все ссылочные типы неявно происходят от System.Object
,присвоение Dog
переменной статического типа object
в порядке. Вы можете думать о «статическом типе» как о том, что объект «объявлен как». Вы всегда можете определить статический тип чего-то , просто прочитав исходный код ;это то, как это делает компилятор.
Тогда есть также тип времени выполнения каждой переменной (выражения), о котором я упоминал выше.Это одинаково в обоих случаях, потому что, в конце концов, в обоих случаях мы создали Dog
. Вы можете думать о «типе времени выполнения» как о том, чем на самом деле является объект . Тип времени выполнения чего-либо не может быть определен просто путем чтения источника;Вы определяете его только во время работы программы, отсюда и название.В C # это делается путем вызова GetType
.
Должно быть очевидно, что тип времени выполнения - это то, без чего вы не можете обойтись¹; все в конце концов должно быть чем-то.Но зачем изобретать понятие статического типа?
Вы можете думать о статических типах как о договоре между вами (программистом) и компилятором.Объявляя статический тип b
равным Dog
, вы сообщаете компилятору, что не собираетесь использовать эту переменную для хранения чего-либо, кроме Dog
.Компилятор, в свою очередь, обещает не позволить вам нарушить вашу заявленную цель и выдает ошибку, если вы пытаетесь это сделать.Он также не позволяет вам использовать d
любым способом, который не должен поддерживаться каждым типом Dog
.
Обратите внимание:
class Dog {
public void Woof();
}
Dog d = new Dog();
d.Woof(); // OK
object o = new Dog();
o.Woof(); // COMPILER ERROR
Последняя строка вызываетошибка компилятора, потому что она нарушает контракт статической типизации: вы сказали компилятору, что o
может быть чем угодно, происходящим от System.Object
, но не все вещи, происходящие от этого, имеют метод Woof
.Таким образом, компилятор пытается защитить вас, говоря: «Что вы там делаете? Я не могу доказать… что все, что в o
может говорить! Что, если бы это было Cat
?».
Примечания:
¹ Это не значит, что каждый объект волшебным образом знает, что он «есть» на всех языках.В некоторых случаях (например, в C ++) эта информация может использоваться при создании объекта, но затем «забывается», чтобы дать компилятору больше свободы для оптимизации кода.Если это происходит, объект все еще является чем-то, но вы не можете ткнуть его и спросить его «кто вы?».
² На самом деле, в этом тривиальном примере это можно доказать.Но он не захочет использовать эти знания, потому что соблюдение контракта статического типа - это весь смысл.