说
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)概念。
答案 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)
如果你看一下课程(不是他们的实例),那么我宁愿画这样的图片:
这意味着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。这是一个内存布局的例子,取自那里:
您可以看到linked lists用于构建从基类实例对象到派生类实例对象的链。
答案 3 :(得分:0)
动物B =新狗();
new运算符返回Dog()的ref;它存储在Animal B中,因此ref b指向dog