3

class Animal
{
  public Animal()
   {
      Console.WriteLine("Animal constructor");
   }
}

class Dog : Animal
{
public Dog()
   {
      Console.WriteLine("Dog constructor");
   }
   }

现在问题来了,如果我写这样的代码

Animal A = new Animal();

在堆中创建了一个对象,我们的引用变量“A”指向堆中的那个位置。

现在如果我写这样的代码

Animal B = new Dog();

那么这里的引用 B 是如何指向对象狗的呢?

我被这个概念的视觉说明所困扰。很多次我读到“我们有一个 Animal 类型的对象,但它引用了一个 Dog 类型的对象”。但它的真正含义是什么?

任何详尽的答案都会很好。因为我正在深入学习很少的 .net (OOPS) 概念。

动物和狗对象

4

4 回答 4

2

参考 B 如何指向对象狗?

引用 A 指向对象 Animal 的方式相同。我认为您可能会混淆类是如何通过继承与实例化相关的。

创建从另一个派生的类的新实例不引用其基类的任何实例,例如

Animal a = new Animal();
Animal b = new Dog();

Animal b没有参考或链接Animal a,它们是type的 2 个独立实例Animal。区别Animal b实际上是类型Dog,但已被强制转换Animal. 当您向上/向下转换继承层次结构时,引用不会改变(认为这方面的技术术语称为引用转换)因此:

Dog d = (Dog)b;

仍然指的是同一个对象Animal b,它只是一种不同类型的引用。

于 2012-08-27T09:55:37.190 回答
1
class Animal
{
    public Animal() 
    { 
        Console.WriteLine("Animal constructor"); 
    }

    public  void  show()        
    {
        Console.WriteLine("animal");
    }

}

class Dog : Animal 
{ 
    public Dog() 
    { 
        Console.WriteLine("Dog constructor"); 
    }

    public  void show()
    {
        Console.WriteLine("Dog");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Animal B = new Dog();

        B.show();
        Console.ReadKey();
    }
}

在上述程序中

B.show();

将调用基类show()

为什么,因为,Dog()也会有show()方法,因为使用的引用类型是 Animal ,所以会调用这个方法,如果你仍然想调用show()Dog 的类,请使用 virtual 和 override 概念

于 2012-08-27T10:52:57.183 回答
1

如果你看一下类(而不是它们的实例),那么我宁愿这样画:

在此处输入图像描述

这意味着Dog类通​​常比类具有更多的方法和属性Animal(例如,一只狗可以bark(方法)和四个legs(属性))。当然,在实例化这个类时必须保留额外的内存。想象一下,首先创建基类方法和属性,然后在内存中创建派生方法和属性:

class Dog : Animal 
{ 
    public Dog() 
    { 
        legs = 4;
        Console.WriteLine("Dog constructor"); 
    } 


    public int legs { get; private set; }

    public void bark()
    {
         Console.WriteLine("grrrwoof!"); 
    }
} 

如果您实例化 aDog并将其分配给Animal引用变量,那么此引用只能访问 aAnimal具有的方法。尽管如此,整个Dog对象仍然保存在内存中:

Dog d = new Dog();
Animal a = (Animal)d;

换句话说,d能够做到以下几点:

Console.WriteLine(String.Format("Number of legs: {0}", d.legs.ToString())); 
d.bark();

a不能这样做,因为这些“特性”没有在Animal类中定义。

现在重要的是要知道并非所有类型的演员表都是允许的。始终允许从 aDog转换为 an Animal,因为这是安全的,但您不能将 an隐式Animal转换为 a Dog,因此以下代码会引发无效的转换异常:

Dog dogRef2 = a; // not allowed

如果您知道自己在做什么(即,如果您确定a包含 的实例Dog),那么您可以显式转换如下:

Dog dogRef2 = (Dog)a; // allowed

之后您可以访问属性和方法:

dogRef2.bark(); // works

这是可行的,因为编译器和运行时总是以相同的结构化方式将方法和属性存储在内存中,并且还会创建一个内部描述符以在引用它时找到它。

请注意,这并不总是安全的,例如,如果您尝试以下操作:

Animal a = new Animal();
Dog dogRef2 = (Dog)a; // Invalid cast exception

为什么?因为new Animal()还没有创建方法bark和属性legs,它只是创建了一个Animal(既不包含属性legs也不包含方法bark)的实例。

更多信息:如果您想了解有关内部结构(如何创建和存储对象)的更多信息,请查看此链接。这是一个内存布局的例子,取自那里: EE类

您可以看到链表用于构建从基类实例对象到派生类实例对象的链。

于 2012-08-27T10:58:58.727 回答
0

动物 B = new Dog();

new 运算符返回 Dog() 的 ref;它存储在 Animal B 中,因此 ref b 指向 dog

于 2012-08-27T10:17:47.623 回答