类型库的引用和派生类的对象是什么意思

时间:2012-08-27 09:46:10

标签: c# polymorphism

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

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

现在问题在于我是否写了这样的代码

Animal A = new Animal();

在堆中创建一个Object,我们的引用变量“A”指向堆中的该位置。

现在如果我写这样的代码

Animal B = new Dog();

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

我被困在这个概念的视觉插图中。很多时候我读过“我们有一个类型为Animal的对象,但它引用了一个类型为Dog的对象”。但这究竟意味着什么?

任何精心设计的答案都会很好。因为我很少学习.net(OOPS)概念。

Animal and Dog Object

4 个答案:

答案 0 :(得分:2)

  

参考B如何指向对象狗?

相同方式参考A指向对象Animal。我认为你可能会混淆类通过继承与实例化相关联。

创建一个派生自另一个类的新实例,并没有引用它的基类的任何实例,例如。

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

Animal b没有Animal a的引用或链接,它们是Animal类型的2个单独的实例。不同之处在于Animal b实际上属于Dog类型,但强制Animal。当您向上/向下转换继承层次结构时,引用不会更改(因此,请将此技术术语称为引用转换):

Dog d = (Dog)b;

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

答案 1 :(得分: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,如果您仍想调用Dog的show(),则会调用此方法s类使用虚拟和覆盖概念

答案 2 :(得分:1)

如果你看一下课程(不是他们的实例),那么我宁愿画这样的图片:

enter image description here

这意味着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!"); 
    }
} 

如果您实例化Dog并将其分配给Animal引用变量,那么此引用只能访问Animal具有的方法。尽管如此,整个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类中定义。

现在重要的是要知道并非所有种类的演员都被允许。始终允许从Dog投射到Animal,因为这是安全的,但您无法隐式地将Animal投射到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,因此它刚刚创建了{{1}的实例(包含属性Animal和方法legs)。

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

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

答案 3 :(得分:0)

动物B =新狗();

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