C#菜鳥之旅-----里氏替換
這是第二次學習“里氏替換”,因為經過了小組的討論和自己今天的研究,感覺對於這個理解更深刻了,於是在學習完之後立刻整理,希望大家可以從這篇部落格中有新的收穫。
對於百度上“大話設計模式”書中對於“里氏替換”的概念都是:
“派生類(子類)物件能夠替換其基類(父類)物件使用” ,而且他們的功能並不會發生變化。
但是為什麼子類就可以替換父類,而且整個程式的功能不會受到影響呢?
原因: 當我們寫的關係滿足繼承的時候,父類中會存在非私有(非private)成員,這個時候,子類肯定是得到了父類的這些非私有成員(假設,父類的成員全部都是私有的,那麼子類沒辦法從父類繼承任何成員,(這裡有的網友也說是被繼承了,只不過是無法訪問)),既然子類繼承了父類的非私有成員,那麼父類物件也就可以在子類物件中呼叫這些非私有成員。
我們先來根據小楊老師看看他講的“里氏替換”的四種轉換的型別:
定義了一個父類 Person,倆個子類:分別是student ,teacher , Int num=10; Double dou=num; 第一種: Person P =new Teacher(); Teacher t =(Teacher) p ; //這是是強制轉換 //如果 new的是子類 ,那麼父類可以轉換為子類 這樣的是可以的 第二種: Person p =new Person(); Student stu=(Student) p ; //如果new的是父類,那麼在轉換子類的時候是不可以的 第三種: Person p =new Teacher() Student stu=(student)p; //結果報異常,new的雖然是子類,但是轉的卻是另外一個子類; //即new 的子類 和 轉換的子類不是同一個類。 第四種: 子類轉換成父類的時候就不需要強制轉換,隱氏轉換就可以了 Student stu=new student() Person p =stu; p.Show(); Console.readkey(); 不會出現錯誤
通過上面小楊老師的講解我們能夠很清晰的清楚,在什麼情況下我們才可以將父類替換成子類---或者說是用子類來替換父類。
下面我們再來通過一個例子來研究一下“里氏替換”:
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,說明父類只可以訪問父類的方法
在經過了
person p1=new student(); 之後 輸入p1.的時候後面也只顯示 say 方法。
所以這裡就滿足了里氏替換原則,子類的student 物件替換了父類person 物件的位置
二,虛方法中的里氏替換。
虛方法: 使用virtual關鍵字修飾的方法,叫作虛方法(一般都是在父類當中)
下面看一段關於虛方法的程式碼:
namespace ConsoleApp45
{
class Program
{
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();
}
}
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("我是一個學生!");
}
}
}
最終結果顯示:
從上面的結果中可以看出,第二個表示式滿足里氏替換原則,p1.say()執行的本該是父類的方法,但是卻執行了子類的say方法
這就是子類使用override關鍵字覆蓋了父類的虛方法
上面的這種情況是在父類的方法中寫了“virtual” 同時子類中的方法也寫了 override
那麼如果父類中用 virtual修飾之後,而子類沒有重寫該方法。結果會是怎麼樣呢?
namespace ConsoleApp45
{
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 方法,就會回到子類的父類去找是否有 virtual並執行