说
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)概念的了解很少时.
解决方法:
如果您看一看这些类(而不是它们的实例),那么我宁愿这样画:
这意味着Dog类通常比Animal类具有更多的方法和属性(例如,狗可以吠叫(方法)并且有四条腿(属性)).当然,在实例化此类时必须保留额外的内存.想象一下,首先创建基类的方法和属性,然后在内存中创建派生的方法和属性:
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();
但不能这样做,因为那些“功能”没有在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和属性leg,所以它只是创建了Animal的实例(既不包含属性leg也不包含方法bark).
更多信息:如果您想了解有关内部结构的更多信息(如何创建和存储对象),请查看this link.这是一个内存布局的示例,从此处获取:
您可以看到linked lists用于构建从基类实例对象到派生类实例对象的链.