分享

【C#小知识】C#中一些易混淆概念总结(六)---------解析里氏替换原则,虚方法

 昵称10504424 2014-02-08

 


强子的专栏
 

再牛逼的梦想,也抵不住傻逼似的坚持!

 

【C#小知识】C#中一些易混淆概念总结(六)---------解析里氏替换原则,虚方法
 


目录:
 
【C#小知识】C#中一些易混淆概念总结--------数据类型存储位置,方法调用,out和ref参数的使用
 
【C#小知识】C#中一些易混淆概念总结(二)--------构造函数,this关键字,部分类,枚举
 
【C#小知识】C#中一些易混淆概念总结(三)--------结构,GC回收,静态成员,静态类
 
【C#小知识】C#中一些易混淆概念总结(四)---------解析Console.WriteLine()
 
【C#小知识】C#中一些易混淆概念总结(五)---------深入解析C#继承
 
----------------------------------------------分割线--------------------------------------------------
 
 
 
这一系列的文章在园子里还是比较受欢迎的。有一些留言指出了其中理论性的错误,还有问自己是否毕业,怎么写出来这些文章的,有没有培训过等等问题。
 
下面就一并的回答这些问题吧。
1)自己今年六月份毕业,现在在帝都实习。不过在学校已经做过一些C#开发了,现在也是做.NET开发工作。
 
2)文章中很多知识是自己以前在网上下载的视频教程,学习过程中所记的笔记。也就是在年前的时候,突然有一天发现自己的笔记本记了差不多块一本了,之前也没时间整理过,所以就想着把它们整理成博客文章,顺便温习一下这些笔记知识。
 
3)有园友问自己是不是在传智培训过。首先说我没有培训过,但是非常感谢传智公开的一些自学教程。因为自己也是这些视频的受益者,学到了很多知识,养成了一些好的学习习惯。
 
4)在整理笔记的过程中遇到了很多问题,其中自己参考了《C#本质论》,《CLR via C#》还有就是MSDN的官方文档。
 
3)不管怎样还是会遇到一些自己解决不掉或者弄不清楚的问题,这个过程使用了Google搜索并和请教了一些园友。
 
4)错误总是会存在。谢谢看我博客的读者你们的细心,指出了我博文中的错误。确定这些错误后,我都立即修改了自己的文章。
 
 
 
---------------------------------------------分割线-----------------------------------------------------
 
 
 
今天开始上班了。这几天研究学习了一下思维导图,感觉用它整理自己的知识非常的方便。所以,以后写博客完成一个知识点,都会用思维导图做一个总结。也能让大家对所要读的内容有一个整体的把握。
 
我用的思维导图软件是FreeMind(免费的,但是得装JDK),因为刚开始学习使用,很多操作技巧不是很熟练,做出来的导图估计也不是很好,希望大家见谅。
 
 
 
首先,里氏替换原则。
 
这是理解多态所必须掌握的内容。对于里氏替换原则维基百科给出的定义如下:
 


 
 
为什么子类可以替换父类的位置,而程序的功能不受影响呢?
 
当满足继承的时候,父类肯定存在非私有成员,子类肯定是得到了父类的这些非私有成员(假设,父类的的成员全部是私有的,那么子类没办法从父类继承任何成员,也就不存在继承的概念了)。既然子类继承了父类的这些非私有成员,那么父类对象也就可以在子类对象中调用这些非私有成员。所以,子类对象可以替换父类对象的位置。
 
来看下面的一段代码:
 


 class Program
    {
        static void Main(string[] args)
        {
            Person p = new Person();

            Person p1 = new Student();

            Console.ReadKey();
        }
    }

    class Person
    {    //父类的私有成员    private int nAge;
        public Person()
        {
            Console.WriteLine("我是Person构造函数,我是一个人!");
        }

        public void Say()
        {
            Console.WriteLine("我是一个人!");
        }

    }

    class Student : Person
    {
        public Student()
        {
            Console.WriteLine("我是Student构造函数,我是一个学生!");
        }

        public void SayStude()
        {
            Console.WriteLine("我是一个学生!");
        }
    }

    class SeniorStudent : Student
    {
        public SeniorStudent()
        {
            Console.WriteLine("我是SeniorStudent构造函数,我是一个高中生!");
        }
        public  void SaySenior()
        {
            Console.WriteLine("我是一个高中生!");
        }
    }


我们运行打印出的结果是:
 


 
 
根据前面的构造函数的知识很容易解释这个结果。那么我们在Main()函数中添加如下的代码:
 


static void Main(string[] args)
        {
            Person p = new Person();
            p.Say();  
            Person p1 = new Student();
            p1.Say();
            Console.ReadKey();
        }


在访问的过程中,可以发现p只可以访问父类的say
 


而p1也只可以访问父类的Say方法
 


其实在上面的代码中,就满足了里氏替换原则。子类的Student对象,替换了父类Person对象的位置。
 
 
 
那么它们在内存中发生了些什么呢?如下图:
 


 
 
由上可以知道,当一个父类的变量指向一个子类对象的时候只能通过这个父类变量调用父类成员,子类独有的成员无法调用。
 
 
 
同理我们可以推理出,子类的变量是不可以指向一个父类的对像的
 


 
 
但是当父类变量指向一个子类变量的时候,可以不可以把父类的变量转化成子类的对象呢?看下图
 


关于引用类型的两种转换方式:
 
 
 
由上面的代码我们已经知道了一种转换,就是在变量钱直接加需要转换的类型,如下代码:
 
Student s2 = (Student)p1;
 
 
那么第二种转换方式就是使用as关键字,如下代码:
 
 //将指向子类对象的变量转化成子类类型
            Student s2 = (Student)p1;

            //使用as关键字,转换失败返回一个null值
            Student s3 = p1 as Student;
使用as关键字和第一种强制转换的区别就是,第一种如果转换失败会抛异常,第二种转换失败则返回一个null值。
 
思维导图总结如下:
 


 
 
 二,虚方法
 
使用virtual关键字修饰的方法,叫做虚方法(一般都是在父类中)。
 
看下面的一段代码:
 
 
 


class Person
    {
        private int nAge;
        public Person()
        {
            Console.WriteLine("我是Person构造函数,我是一个人!");
        }

        //这里定义了一个虚方法
        public virtual void Say()
        {
            Console.WriteLine("我是一个人!");
        }

    }

    class Student : Person
    {
        //子类使用override关键字改写了父类的虚方法
        public override void Say()
        {
            Console.WriteLine("我是一个学生!");
        }
        public Student()
        {
            Console.WriteLine("我是Student构造函数,我是一个学生!");
        }

        public void SayStude()
        {
            Console.WriteLine("我是一个学生!");
        }
    }


 
 
紧接着在main()函数中添加如下的代码:
 


static void Main(string[] args)
        {
            Person p = new Person();
            p.Say();

            Person p1 = new Student();
            p1.Say();

            Student s = new Student();
            s.Say();
            Console.ReadKey();
        }


打印结果如下:
 
 
 


 
 
我们很明显的可以发现,第二个表达式满足里氏替换原则,p1.Say()执行的应该是父类的Say()方法,但是这里却执行了子类的Say()方法。
 
这就是子类使用override关键字的Say()方法覆盖了父类的用Virtual关键字修饰的Say()方法。
 
我们使用动态图片看一下调试过程,
 
①首先是没有使用任何关键字:
 


 
 
由上可以看出直接跳入父类,执行了父类的Say()方法;
 
 
 
②再看使用virtual和override关键字的动态调试图片,如下:
 


可以看到直接到子类去执行override关键字修饰的Say()方法。
 
 
 
那么如果父类使用virtual关键字修饰,而子类没有重写该方法时会怎么样呢?如下面的代码:
 
 
 


class Program
    {
        static void Main(string[] args)
        {
          
            Person p1 = new Student();
            p1.Say();
            Console.ReadKey();
        }
    }

    class Person
    {
        private int nAge;
        public Person()
        {
            Console.WriteLine("我是Person构造函数,我是一个人!");
        }

        //这里定义了一个虚方法
        public virtual void Say()
        {
            Console.WriteLine("我是一个人!");
        }

    }

    class Student : Person
    {
        //子类中没有出现override关键字修饰的方法
        public void SayStude()
        {
            Console.WriteLine("我是一个学生!");
        }
    }


 
 
执行结果如下:
 
 
 


 
 
 
 
所以,如果子类找不到override方法,则会回溯到该子类的父类去找是否有override方法,知道回溯到自身的虚方法,并执行。
 
虚方法知识总结的思维导图如下:
 

 

--------------------------------------------------------------------------------

如果您觉得不错,点击右下角,推荐一下吧!您的支持,是我写作的动力!
 
毕业实习交流群:221376964。你也可以关注我的新浪微博进行交流。
 

 

分类: C#学习+开发
 

绿色通道: 好文要顶 关注我 收藏该文与我联系

 

强_子
 关注 - 9
 粉丝 - 140

 

+加关注


0

0


(请您对文章做出评价)


上一篇:【C#小知识】C#中一些易混淆概念总结(五)---------深入解析C#继承

 
posted on 2014-02-08 01:52 强_子 阅读(85) 评论(1) 编辑 收藏
 

 

评论
 
#1楼 2014-02-08 04:57 拿笔小心  

 

写的不错,就是错别字太多

支持(0)反对(0)
   

 


刷新评论刷新页面返回顶部
 

注册用户登录后才能发表评论,请 登录 或 注册,访问网站首页。
 
程序员问答平台,解决您的技术难题
 
博客园首页博问新闻闪存程序员招聘知识库
 

 

 


最新IT新闻:
 · 回归企业业务将挽救微软的未来十年
 · 微信支付场景分析:财付通是QQ场景,而非微信
 · 魅族布道师的归回
 · 全球癌症发病率不断增长
 · Windows PC 厂商面临的价值泥潭
更多新闻...

最新知识库文章:

 · 浏览器中关于事件的那点事儿
 · 全栈工程师就是一棵歪脖子树
 · Linux上的基础网络设备详解
 · 深入剖析阿里巴巴云梯YARN集群
 · 关于技术团队管理的胡言乱语

更多知识库文章...

 

导航
 博客园
 首页
 联系
 订阅
 管理
 

 

 


<

2014年2月

>

 


 

26

27

28

29

30

31

1

 

2

3

4

5

6

7

8

 

9

10

11

12

13

14

15

 

16

17

18

19

20

21

22

 

23

24

25

26

27

28

1

 

2

3

4

5

6

7

8

 
公告
 


昵称:强_子
园龄:2年5个月
粉丝:140
关注:9
+加关注
 

统计
 随笔 - 37
文章 - 0
评论 - 396

 

搜索
 
 
 
 
 
常用链接
 我的随笔
 我的评论
 我的参与
 最新评论
 我的标签
 


随笔分类(40)
 ASP.NET开发(10)
C#学习+开发(12)
C语言
HTML+CSS知识
JavaScript+Ajax开发(1)
MSSQLServer(4)
程序人生(13)
错误积累
转载收藏

随笔档案(37)
 2014年2月 (6)
2014年1月 (5)
2013年12月 (2)
2013年11月 (1)
2013年8月 (4)
2013年7月 (1)
2013年1月 (3)
2012年11月 (1)
2012年10月 (8)
2012年9月 (3)
2012年5月 (2)
2011年11月 (1)

最新评论
 

1. Re:【C#小知识】C#中一些易混淆概念总结(六)---------解析里氏替换原则,虚方法
 写的不错,就是错别字太多 --拿笔小心
 2. Re:【C#小知识】C#中一些易混淆概念总结(四)---------解析Console.WriteLine()
 @dotnetgeek以前网上下载的视频教程记得笔记。然后写的时候有参考了,C#本质论,MSDN,和Google的资料写的 --强_子
 3. Re:【C#小知识】C#中一些易混淆概念总结(五)---------深入解析C#继承
 @Losofiya是的呀, --强_子
 4. Re:【C#小知识】C#中一些易混淆概念总结(五)---------深入解析C#继承
 @codezyc是使用这个小工具,是可以下载,傻瓜式操作,谷歌也有教程
http://www./licecap/你可以下载 --强_子
 5. Re:【C#小知识】C#中一些易混淆概念总结(五)---------深入解析C#继承
 @garfieldzf大哥,好吧,是“软文” --强_子
 
阅读排行榜
 

1. 第一次实习面试感受----苦逼程序员生活初体验(7107)
2. 【ASP.NET开发】.NET三层架构简单解析(3195)
3. 网络工作室暑假后第三次培训资料(几种SQL分页的总结)整理(2971)
4. 解析ASP.NET WebForm和Mvc开发的区别(2869)
5. 从明源动力到创新工场这一路走来(2724)
 
评论排行榜
 

1. 第一次实习面试感受----苦逼程序员生活初体验(102)
2. 网络工作室暑假后第三次培训资料(几种SQL分页的总结)整理(43)
3. 【ASP.NET开发】.NET三层架构简单解析(28)
4. 从明源动力到创新工场这一路走来(26)
5. 解析ASP.NET WebForm和Mvc开发的区别(25)
 
推荐排行榜
 

1. 第一次实习面试感受----苦逼程序员生活初体验(35)
2. 解析ASP.NET WebForm和Mvc开发的区别(23)
3. 从明源动力到创新工场这一路走来(23)
4. 【C#小知识】C#中一些易混淆概念总结---------数据类型存储,方法调用,out和ref参数的使用(16)
5. 实习第一周小记------生活不易(10)
 
Powered by:
博客园
Copyright ? 强_子

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多