object a = new Dog();
VS
Dog a = new Dog();
在这两种情况下,a.GetType()
都会Dog
。
两者都调用相同的构造函数(具有相同的层次结构)。
那么请你告诉我这两个陈述之间的区别吗?
答案 0 :(得分:36)
两者都创建一个Dog对象。只有第二个允许您直接调用Dog方法或以其他方式将其视为狗,例如,如果您需要将对象作为Dog
类型的参数传递给方法(或者Dog层次结构中的某些内容)更具体而不仅仅是object
)。
object obj = new Dog();
// can only see members declared on object
var type = obj.GetType(); // can do this
Console.WriteLine(obj.ToString()); // also this
obj.Bark(); // Error! Bark is not a member of System.Object
Dog dog = new Dog();
// can do all of the methods declared for Object
dog.Bark(); // can finally use the method defined for Dog
答案 1 :(得分:6)
new Dog()
是一个创建新Dog
实例的表达式。它调用Dog
类的无参数构造函数。
a
是一个变量:内存中的存储单元,在分配后保存对Dog
实例的引用。
不同之处在于Dog
变量只能保存对Dog
实例(或从Dog
派生的任何类的实例)的引用,而object
变量可以保存对object
实例的引用(或者是object
派生的任何类的实例 - Dog
类的实例)。
如果有Dog
变量,则可以在引用的实例上调用Dog
类(及其基类)定义的任何方法。如果有object
变量,则只能在实例上调用object
类的方法。
答案 2 :(得分:5)
您的第一行创建了object
类型的变量。
编译器不允许您将其视为Dog
。
答案 3 :(得分:5)
两个语句都包含声明和构造函数调用。构造函数的调用是相同的,因此在两种情况下都会得到Dog
。声明是不同的:在第一种情况下,您声明一个object
类型的变量,一个超级类Dog
;在第二种情况下,您声明一个类型为Dog
的变量。不同之处在于,在后续代码中,只有在将变量声明为Dog
时,才能调用Dog
的方法而不进行强制转换。如果你将其声明为object
,则需要演员。
答案 4 :(得分:4)
两个语句都涉及调用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
由于所有 reference types 都隐式派生自System.Object
,因此将Dog
分配给静态类型object
的变量即可。 您可以将“静态类型”视为对象“声明为”的内容。您只需通过阅读源代码即可确定的静态类型;这就是编译器的工作方式。
然后还有每个变量(表达式)的运行时类型,我在上面提到过。这两种情况都是一样的,因为毕竟在这两种情况下我们都创建了Dog
。 您可以将“运行时类型”视为对象实际 。只能通过读取源来确定某些内容的运行时类型;您只在程序运行时确定它,因此名称。在C#中,这是通过调用GetType
来完成的。
很明显,运行时类型是你不能没有的东西¹;毕竟所有必须“成为”某事。但为什么要发明静态类型的概念呢?
您可以将static types视为您(程序员)与编译器之间的契约。通过将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 ++中),这些信息可能在创建对象时使用,但随后被“遗忘”,以便编译器可以更自由地优化代码。如果发生这种情况,对象仍然 ,但你不能戳它并问它“你是什么?”。
²实际上,在这个琐碎的例子中,它可以证明这一点。但它不会选择使用这些知识,因为尊重静态类型的契约就是重点。
答案 5 :(得分:2)
当您想要使用多态时,这很有用,您可以使用在Dog中实现的抽象方法。因此,这样对象就是Dog,即使是Object也是如此。因此,当您想使用多态时,可以使用这种方式。