对象之间的区别a = new Dog()vs Dog a = new Dog()

object a = new Dog(); 

VS

 Dog a = new Dog(); 

在这两种情况下, a.GetType()给出了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实例的引用(或者派生的任何类的实例)来自objectDog类所做的事。

如果有Dog变量,则可以在引用的实例上调用Dog类(及其基类)定义的任何方法。 如果有object变量,则只能在实例上调用object类的方法。

第一行创建一个object类型的变量

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

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

这两个语句都涉及到你自己提到的调用Dog的默认构造函数; 因此,显然在两种情况下都构建了Dog实例。 这意味着两个语句最终都使用相同的实例初始化变量 (这是等于后的语句的一部分)。

但是,这些语句还有另一部分: 变量声明 (这是在equals之前声明的一部分)。 在诸如C#的静态类型语言中,每个变量 – 更一般地说,任何表达式 – 都具有静态类型:

 object a = new Dog(); // static type: object / runtime type: Dog Dog b = new Dog(); // static type: Dog / runtime type: Dog 

编译器不允许您为变量赋值,它无法certificate变量的静态类型,例如它不允许

 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 。 因此,编译器试图通过说“你在那里做什么来保护你?我无法certificatethat无论什么都可以了!如果它是Cat ?”。

笔记:

¹这并不意味着每个对象都神奇地知道所有语言中的“是”。 在某些情况下(例如在C ++中),这些信息可能在创建对象时使用,但随后被“遗忘”,以便编译器可以更自由地优化代码。 如果发生这种情况,对象仍然某种东西,但你不能戳它并问它“你是什么?”。

²实际上,在这个微不足道的例子中,它可以certificate这一点。 但它不会选择使用这种知识,因为尊重静态类型的契约是重点。

当您想要使用多态时,这很有用,您可以使用在Dog中实现的抽象方法。 因此,这样对象就是Dog,即使是Object也是如此。 因此,当您想要使用多态时,可以使用这种方式。