C#菜鸟之旅-----里氏替换

矫情吗;* 2022-04-10 12:53 282阅读 0赞
  1. 这是第二次学习“里氏替换”,因为经过了小组的讨论和自己今天的研究,感觉对于这个理解更深刻了,于是在学习完之后立刻整理,希望大家可以从这篇博客中有新的收获。
  2. 对于百度上“大话设计模式”书中对于“里氏替换”的概念都是:
  3. **“派生类(子类)对象能够替换其基类(父类)对象使用” ,而且他们的功能并不会发生变化。**
  4. 但是为什么子类就可以替换父类,而且整个程序的功能不会受到影响呢?
  5. **原因: 当我们写的关系满足继承的时候,父类中会存在非私有(非private)成员,这个时候,子类肯定是得到了父类的这些非私有成员(****假设,父类的成员全部都是私有的,那么子类没办法从父类继承任何成员,(这里有的网友也说是被继承了,只不过是无法访问)),****既然子类继承了父类的非私有成员,那么父类对象也就可以在子类对象中调用这些非私有成员。**
  6. 我们先来根据小杨老师看看他讲的“里氏替换”的四种转换的类型:
  7. 定义了一个父类 Person,俩个子类:分别是student teacher
  8. Int num=10;
  9. Double dou=num;
  10. 第一种:
  11. Person P =new Teacher();
  12. Teacher t =(Teacher) p ; //这是是强制转换
  13. //如果 new的是子类 ,那么父类可以转换为子类 这样的是可以的
  14. 第二种:
  15. Person p =new Person();
  16. Student stu=(Student) p ;
  17. //如果new的是父类,那么在转换子类的时候是不可以的
  18. 第三种:
  19. Person p =new Teacher()
  20. Student stu=(student)p;
  21. //结果报异常,new的虽然是子类,但是转的却是另外一个子类;
  22. //即new 的子类 和 转换的子类不是同一个类。
  23. 第四种:
  24. 子类转换成父类的时候就不需要强制转换,隐氏转换就可以了
  25. Student stu=new student()
  26. Person p =stu;
  27. p.Show();
  28. Console.readkey();
  29. 不会出现错误

通过上面小杨老师的讲解我们能够很清晰的清楚,在什么情况下我们才可以将父类替换成子类—-或者说是用子类来替换父类。

下面我们再来通过一个例子来研究一下“里氏替换”:

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Person p = new Person();
  6. Person p1 = new Student();
  7. Console.ReadKey();
  8. }
  9. }
  10. class Person
  11. {
  12.     //父类的私有成员
  13.     private int nAge;
  14. public Person()
  15. {
  16. Console.WriteLine("我是Person构造函数,我是一个人!");
  17. }
  18. public void Say()
  19. {
  20. Console.WriteLine("我是一个人!");
  21. }
  22. }
  23. class Student : Person
  24. {
  25. public Student()
  26. {
  27. Console.WriteLine("我是Student构造函数,我是一个学生!");
  28. }
  29. public void SayStude()
  30. {
  31. Console.WriteLine("我是一个学生!");
  32. }
  33. }
  34. class SeniorStudent : Student
  35. {
  36. public SeniorStudent()
  37. {
  38. Console.WriteLine("我是SeniorStudent构造函数,我是一个高中生!");
  39. }
  40. public void SaySenior()
  41. {
  42. Console.WriteLine("我是一个高中生!");
  43. }
  44. }

运行后的结果是:

20190107131928114.png

然后我们在Main()函数中添加一些代码其他的地方不改变:

  1. static void Main(string[] args)
  2. {
  3. Person p = new Person();
  4. p.Say();
  5.   
  6. Person p1 = new Student();
  7. p1.Say();
  8. Console.ReadKey();
  9. }

继续运行,结果是:

20190107132046738.png

而且在我输入代码的过程中,一个细节我发现,当我输入p.的时候,它后面的方法只有say,说明父类只可以访问父类的方法

2019010713220566.png

在经过了

person p1=new student(); 之后 输入p1.的时候后面也只显示 say 方法。

20190107132546363.png

所以这里就满足了里氏替换原则,子类的student 对象替换了父类person 对象的位置

二,虚方法中的里氏替换。

虚方法: 使用virtual关键字修饰的方法,叫作虚方法(一般都是在父类当中)

下面看一段关于虚方法的代码:

  1. namespace ConsoleApp45
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. Person p = new Person();
  8. p.Say();
  9. Person p1 = new Student();
  10. p1.Say();
  11. Student s = new Student();
  12. s.Say();
  13. Console.ReadKey();
  14. }
  15. }
  16. class Person
  17. {
  18. private int nAge;
  19. public Person()
  20. {
  21. Console.WriteLine("我是Person构造函数,我是一个人!");
  22. }
  23. //这里定义了一个虚方法
  24. public virtual void Say()
  25. {
  26. Console.WriteLine("我是一个人!");
  27. }
  28. }
  29. class Student : Person
  30. {
  31. //子类使用override关键字改写了父类的虚方法
  32. public override void Say()
  33. {
  34. Console.WriteLine("我是一个学生!");
  35. }
  36. public Student()
  37. {
  38. Console.WriteLine("我是Student构造函数,我是一个学生!");
  39. }
  40. public void SayStude()
  41. {
  42. Console.WriteLine("我是一个学生!");
  43. }
  44. }
  45. }

最终结果显示:

20190107134906275.png

从上面的结果中可以看出,第二个表达式满足里氏替换原则,p1.say()执行的本该是父类的方法,但是却执行了子类的say方法

这就是子类使用override关键字覆盖了父类的虚方法

上面的这种情况是在父类的方法中写了“virtual” 同时子类中的方法也写了 override

那么如果父类中用 virtual修饰之后,而子类没有重写该方法。结果会是怎么样呢?

  1. namespace ConsoleApp45
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. Person p1 = new Student();
  8. p1.Say();
  9. Console.ReadKey();
  10. }
  11. }
  12. class Person
  13. {
  14. private int nAge;
  15. public Person()
  16. {
  17. Console.WriteLine("我是Person构造函数,我是一个人!");
  18. }
  19. //这里定义了一个虚方法
  20. public virtual void Say()
  21. {
  22. Console.WriteLine("我是一个人!");
  23. }
  24. }
  25. class Student : Person
  26. {
  27. //子类中没有出现override关键字修饰的方法
  28. public void SayStude()
  29. {
  30. Console.WriteLine("我是一个学生!");
  31. }
  32. }
  33. }

结果显示:

20190107135545171.png

所以,如果子类中找不到 override 方法,就会回到子类的父类去找是否有 virtual并执行

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMwNjMxMDYz_size_16_color_FFFFFF_t_70

发表评论

表情:
评论列表 (有 0 条评论,282人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C#-----里氏替换

         这是第二次学习“里氏替换”,因为经过了小组的讨论和自己今天的研究,感觉对于这个理解更深刻了,于是在学习完之后立刻整理,希望大家可以从这篇博客中有新的收获。    

    相关 C#-----C#字符串

    命名空间(namespace): 用于解决类重名的问题,可以看做是“类的文件夹” 如果代码和被使用的类在一个namespace中则不需要使用Using 在不同的命名空间下的

    相关 C#-----C# 类

    问大家一个问题: 在这个世界,究竟是先有对象?还是先有类? 哈哈,看到这个有没有想起小时候一直在纠结的“先有鸡还是先有蛋?” ,网友们的答案也是五花八门 不过下面这位

    相关 C#-----C#方法

    方法(函数)介绍:  函数就是将一对代码进行重用的一种机制,函数就是一段代码,这个函数用来执行一个任务的语句块 每一个C\程序有一个带有Main方法的类 这段代码可能

    相关 C#-----集合

    在学习集合之前我们需要先来了解一下集合:        集合,表示可以通过遍历每个元素来访问一组对象(特别是可使用foreach循环访问)。 集合和数组的区别: