对象a = new Dog()与狗a = new Dog()的区别

本文关键字:new Dog 区别 与狗 对象 | 更新日期: 2023-09-27 18:18:27

object a = new Dog();

Dog a = new Dog();

两种情况下,a.GetType()得到Dog。两者调用相同的构造函数(具有相同的层次结构)。

那你能告诉我这两种说法的区别吗?

对象a = new Dog()与狗a = new Dog()的区别

都创建一个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

new Dog()是一个新建Dog实例的表达式。它调用Dog类的无参数构造函数。

a是一个变量:内存中的存储单元,在赋值后保存对Dog实例的引用。

不同之处在于,Dog变量只能保存对Dog实例的引用(或从Dog派生的任何类的实例),而object变量可以保存对object实例的引用(或从object派生的任何类的实例,Dog类可以)。

当您有一个Dog变量时,您可以调用Dog类(及其基类)在引用实例上定义的任何方法。当您有一个object变量时,您只能在实例上调用object类的方法。

第一行创建了一个类型为object变量

编译器不会让你把它当作Dog .

两个语句都包含一个声明和一个构造函数调用。构造函数的调用是相同的,因此在两种情况下都得到Dog。声明是不同的:在第一种情况下,您声明了object类型的变量,Dog的超类;在第二种情况下,声明一个Dog类型的变量。不同之处在于,在随后的代码中,只有在将变量声明为Dog时,才能调用Dog的方法而不进行强制类型转换;如果将其声明为object,则需要进行强制转换。

这两个语句都涉及调用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以外的任何内容。作为回报,编译器承诺不会让您违反所声明的目的,如果您试图违反,则会产生错误。它还可以防止您以不是每种类型的Dog都应该支持的任何方式使用d

考虑:

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++中),这些信息可能在创建对象时使用,但随后被"遗忘",以便让编译器更自由地优化代码。如果发生这种情况,对象仍然什么的,但是你不能戳它并问它"你是什么?"

²实际上,在这个简单的例子中它可以证明。但它不会选择使用这些知识,因为遵守静态类型的契约才是关键。

当您想要使用多态性并且可以使用在Dog中实现的抽象方法时,这很有用。因此,这样说来,对象就是狗,对象也是。因此,当您想要使用多态性时,可以使用这种方式。