继承与多态

最近,拜读了王涛的《你必须知道的.net》前一章节,感受颇多,如此复杂的原理,在王涛的谈笑之间,不知不觉已被王涛诱惑于无形,由对象的产生,讲到对象的继承,封装,再到多态,再引申到模式,简直到了出神入化地步,心里又是妒忌,又是窃喜...

    下面,我就“多态”的一些体验写下来,至于对象的又生到死的过程,这里就不讲了。

    生物界,生命形式多姿多彩,无奇不有,无处不体现着多态,生物界的多种生态形式,归根到底是因为生物具有一定的“遗传性”,也许你长得比你弟弟要矮些,但不一定是你弟弟吃的东西比你好,很有可能,是”遗传“在作怪,或者某个“长得高的变量基因”没有遗传给你,或许是你的访问权限不够,那也是听天又命的事情。那么程序的世界里会是什么样子呢?具体的实现机制又是怎样的呢?

    在此之前,我们得给程序世界的"多态性",定一个量,要不然就会太泛,当然,有部分概论是借鉴王涛先生的,请大家体谅。

    多态指,同一类事物,同一方法,能表现不同的形式,比如抽象方法覆写,方法重写等等,都是多态的表现,我们来看一个简单的例子。

例一


namespace _1_2_Inheritance
{
    
public abstract class A
    
{
        
public string name = "A_name";

        
public abstract void DoAnythings();
    }


    
public class B : A
    
{
        
public string name = "B_name";

        
public override void DoAnythings()
        
{
            Console.WriteLine(
"B name is {0}", name);
        }

    }


    
public class C : B
    
{
        
public string name = "C_name";

        
public override void DoAnythings()
        
{
            Console.WriteLine(
"C name is {0}", name);
        }

    }


    
public class TestInher
    
{
        
public static void Main()
        
{
            A c 
= new C();
            c.DoAnythings();
            Console.WriteLine(c.name);
            Console.ReadLine();
        }


    }


}

代码实在太简单,我都不好意思讲,说实话,在我没有看《你必须知道的.net》之前,我一直都认为c.name等于“C_name”,而且深信不疑。

    还是先简单的介绍一下.net的堆和栈机制,栈一般存放“值类型”,当然包括指针pointer,而且栈内存又系统负责回收,Clr的Gc也不用管,栈主要保存一些方法的调用指令,以及一些局部变量。也就是说,程序代码中的所有方法都会被“压栈”,就好比我们经常吃的“柠檬口味的乐事”,都是一片一片,整整齐齐又上往下压的。

    “引用类型” 的对象分配比较有意思,先在栈中,分配一个指针空间,此时为null,然后再堆中任意开辟一块内存空间,保持实实在在的数据,然后把堆的地址赋值给栈中的空指针,所以我们要明白一点,”值类型“是和指针完全不同的两个概念,只是他俩都存在栈中而已。堆中的数据需要Gc负责回收,所以我们能少用“引用类型”就要少用,少装点箱子,少开点箱子。

    我们来看看“栈内存”中的真实生活

请大家注意左上角第一行数据“0012FFC4”,请大家记住他的位置是第一行top one,我们来单步调试一下,再看下一张图。

单步调试后的“0012FFC4”已经排在top two了,第一行的栈地址是“0012FFC0”,也就是0012FFC4减去4,所以“压栈“是真实的,我绝不会忽悠大家。


    大家对堆和栈有一个比较直观的认识后,我们来看看C,B类型对象的变量和方法的分配情况,因为A是抽象的,所以不能直接构造对象,所以不在讨论范围,通过调试,获得一张B和C类型对象的变量和方法的分布图

同名变量情况, 从上到下,也就是压栈


抽象和虚方法,非抽象和虚方法

 

从图中,我们可以看出,从上到下,从子类到父类的变量依次被初始化,而且子类中都继承和分配了父类的变量,如果方法是抽象的,那么子类直接拷贝父类抽象方法的签名,并根据子类不同的方法实现,来重写父类的抽象方法。

你必须知道的.net中,处理同名变量和抽象方法,有两个原则,一是”对象创建原则“,和“就近原则”,其实都可以归纳为"就近原则”,再联想一下这张图,还有什么不可以搞定呢?

总结

    继承,封装,多态等这些概念,只有类比到现实世界中的种种,才可能理解得更透彻,希望对大家有些帮助,欢迎大家讨论,指教...

补充和纠正

    晚上看到了大家的评语,很贴切,也很有建设性,谢谢。

就在刚刚,我和王涛先生聊了一些关于,virtual方法和abstract方法和普通方法的内存分布情况,谢谢王涛先生指正一个非常普通,但又容易犯错误的问题。

纠正1,非virtual方法和非abstract方法,在子类中都没有父类的拷贝。

纠正2, 只要是virtual方法和abstract方法,不管子类中有没有override父类的方法,子类中都有父类的virtual方法和abstract方法的拷贝。

原文地址:https://www.cnblogs.com/tangself/p/1955887.html