一:前言
在本系列課程的第一部分,我們說明為了要選擇C#作為你成為程序員的第一門語言:
? 首先,C#是一門非常優秀的面向對象編程的語言;
凡是對編碼感興趣的同學一定聽說過“面向對象編程”這個概念,C#就是為此誕生的,它天然是面向對象的。所以,既然“面向對象編程”是當前IT界的主流,我們選擇C#就沒有偏離主流方向。
本節,我們就要講講什么是面向對象,以及面向對象開發中最重要,最應該掌握的概念。
二:什么是面向對象
“面向對象”是當前軟件開發的一個主流思想,它有三個主要特征:封裝、繼承、多態。很多軟件開發的教程使用了阿貓阿狗的例子來講面向對象,其實說明了“面向對象”在某種意義上是很好理解的概念。我們已經在之前的例子中講過了“類”這個概念,現在我們就來講講抽象類,接口。當然,我們不打算用阿貓阿狗的例子(阿貓阿狗都是動物,動物都有吃喝拉撒的習慣……,但是阿貓有阿貓的喵喵叫,阿狗有阿狗的汪汪叫……),我們在這里舉一個實際的例子,只不過,需要大家活動思維,主動將概念聯系到阿貓阿狗中去。
這個實際的例子是.Net Framework基礎類庫(以后簡稱:FCL)中的三個類Stream、FileStream 、MemoryStream ,如下:
public abstract class Stream
{
public abstract void Write(byte[] buffer, int offset, int count);
}public class FileStream : Stream
{
public override void Write(byte[] array, int offset, int count)
{
}
}public class MemoryStream : Stream
{
public override void Write(byte[] buffer, int offset, int count)
{
}
}
這三個是負責byte讀寫的類,我只拿出了其中一個方法Write。
(Tip:什么是byte?,程序中的數據會以byte的形式存在,我們只要知道,讀寫內容,如字符串、整數,到內存中,到文件中,實際讀寫的就是byte就行了)
1:什么是封裝?
一句話概括:像上面這樣,將代碼分類到不同的類中,實際就是封裝。
2:什么是繼承?
像上面這樣,有父類Stream,有兩個子類,就叫做繼承。如果要講繼承,我們就需要講解下訪問限制符了。
2.1什么是類型的訪問限制符?
在C#中,可以為類型,如Stream類,或者類型的成員,如Write方法,添加訪問限制符。在C#的世界中,我們將類型和類型的成員所在的可見范圍分為如下幾類(注意,只講解最常用的):
a)在整個應用程序內可見
使用public修飾。
b)在當前項目內可見
使用internal修飾。
c)在當前類型內部可見
使用PRivate修飾。該修飾符主要修飾類型的成員。
d)僅在子類中可見
使用protected修飾。該修飾符主要修飾類型的成員。
需要強調的是,類型的默認訪問限制符是internal,如果我們不加任何修飾符,.NET編譯器就會默認此類型為internal。類型的成員的訪問限制符是private。
好了,現在不妨停下來,自己創建一些類型和類型成員,感悟下對訪問限制符的理解吧。
2.2什么是抽象類?
如何創建一個類的對象?使用new,如下:
Stream stream = new Stream();
但是這段代碼你一定執行不了,因為Stream是抽象類,它被abstract修飾著。既然抽象類new不了?那它有什么意義呢?
a)首先,它可以定義一些抽象的方法abstract method,抽象方法表達了這樣一個意思:嗨,子類,你必須實現我這個方法;
b)其次,抽象類可以有一些常規的非abstract的方法,也就是說,我們可以把一些子類中相同的邏輯,放到這個抽象方法中,這樣就不需要相同的代碼在不同的子類中都去寫一遍了。
2.3什么是接口?
如果一個抽象類,只有抽象方法,而沒有常規方法,那么我們可以考慮將它抽象為接口(interface)。一個簡單的接口的例子如下:
interface IDispose
{
void Dispose();
}
那么,什么時候用接口,什么時候用抽象類呢?這個問題問的很好,但是,別著急,作為初學者,我們是很難把握這個標準的,對于現在的我們來說,我們只要直接.NET的世界中,存在類、抽象類、接口就可以了,嘗試一口氣全部弄明白,往往屬于揠苗助長。
3:什么是多態?
一句話概括之:讓子類有自己的行為,就是多態。好吧,其實,多態可不是一句話能概括的,我們來具體看看多態的具體含義及實現手段。
三:多態的具體含義及實現手段
備注:以下內容摘自我所撰寫的書《高質量代碼編寫:改善C#的157個建議》。注意,要完全掌握多態的含義及實現手段,必須通過自己編寫代碼細細品味才行,請按照本文下面的例子來體會。
override和new使我們的類型體系因為繼承而呈現出多態性。多態是一門語言是否是“面向對象語言”的三個重要特性之一。多態要求子類具有與基類方法同名的方法,而override和new的作用就是:
如果子類中的方法前面帶有new關鍵字,則該方法被定義為獨立于基類的方法。
如果子類中的方法前面帶有override關鍵字,則子類的對象將調用該方法,而不是調用基類方法。
這兩個定義看上去有些抽象,要深刻理解它們之間的區別,我們不妨來看一個繼承體系:
public class Shape
{
public virtual void MethodVirtual()
{
Console.WriteLine("base MethodVirtual call");
}public void Method()
{
Console.WriteLine("base Method call");
}
}class Circle : Shape
{
public override void MethodVirtual()
{
Console.WriteLine("circle override MethodVirtual");
}
}class Rectangle : Shape
{
}class Triangle : Shape
{
public new void MethodVirtual()
{
Console.WriteLine("triangle new MethodVirtual");
}public new void Method()
{
Console.WriteLine("triangle new Method");
}}
class Diamond : Shape
{
public void MethodVirtual()
{
Console.WriteLine("Diamond default MethodVirtual");
}public void Method()
{
Console.WriteLine("Diamond default Method");
}
}
查看上面的繼承體系,類型Shape是所有子類的基類。
Circle類override了父類的MethodVirtual,所以即使子類轉型為了Shape,調用的還是子類的方法:
Shape s = new Circle()
s.MethodVirtual();
s.Method();
輸出:
circle override MethodVirtual
base Method call
當然,Circle的第二種用法很好理解。使用子類本身的類型,調用的則全部是子類的方法,如下所示:
Circle circle = new Circle();
circle.MethodVirtual();
circle.Method();
輸出也是:
circle override MethodVirtual
base Method call
類型Rectangle沒有對基類做任何處理,所以無論子類是否轉型為Shape,調用的都是基類Shape的方法。
類型Triangle將基類Shape的virtual方法和非virtual方法都new了一遍。所以第一種用法為:
Shape s = new Triangle()
s.MethodVirtual();
s.Method();
因為子類已經new了父類的方法,故子類方法和基類方法完全沒有關系了,只要s被轉型為Sharp,則針對s調用的都是父類方法。
第二種用法很好理解,調用的都是子類的方法,如下所示:
Triangle triangel = new Triangle();
triangel.MethodVirtual();
triangel.Method();
輸出:
triangle new MethodVirtual
triangle new Method
類型Diamond,包含了兩個和基類一模一樣的方法,并且沒有額外的修飾符。這在編輯器中會提出警示。但是如果選擇忽略這些警示,程序一樣還是可以運行。它的第一種用法為:
Shape s = new Diamond()
s.MethodVirtual();
s.Method();
編譯器會默認new的效果,所以輸出和顯式new修飾的一樣。
輸出:
base MethodVirtual call
base Method call
在Diamond的第二種用法中,全部調用的是子類的方法,如下所示:
Diamond diamond = new Diamond();
diamond.MethodVirtual();
diamond.Method();
輸出:
Diamond default MethodVirtual
Diamond default Method
我們總結一下以上所有的用法,并給出一個綜合示例,讀者可以仔細體會每種用法所帶來的輸出變化:
static void Main(string[] args)
{
TestShape();
TestDerive();
TestDerive2();
}private static void TestShape()
{
Console.WriteLine("TestShape/tStart");
List<Shape> shapes = new List<Shape>();
shapes.Add(new Circle());
shapes.Add(new Rectangle());
shapes.Add(new Triangle());
shapes.Add(new Diamond());
foreach (Shape s in shapes)
{
s.MethodVirtual();
s.Method();
}Console.WriteLine("TestShape/tEnd/n");
}private static void TestDerive()
{
Console.WriteLine("TestDerive/tStart");
Circle circle = new Circle();
Rectangle rectangle = new Rectangle();
Triangle triangel = new Triangle();
Diamond diamond = new Diamond();
circle.MethodVirtual();
circle.Method();
rectangle.MethodVirtual();
rectangle.Method();
triangel.MethodVirtual();
triangel.Method();
diamond.MethodVirtual();
diamond.Method();
Console.WriteLine("TestShape/tEnd/n");
}private static void TestDerive2()
{
Console.WriteLine("TestDerive2/tStart");
Circle circle = new Circle();
PrintShape(circle);
Rectangle rectangle = new Rectangle();
PrintShape(rectangle);
Triangle triangel = new Triangle();
PrintShape(triangel);
Diamond diamond = new Diamond();
PrintShape(diamond);
Console.WriteLine("TestDerive2/tEnd/n");}
static void PrintShape(Shape sharpe)
{
sharpe.MethodVirtual();
sharpe.Method();
}
輸出:
TestShape Start
circle override MethodVirtual
base Method call
base MethodVirtual call
base Method call
base MethodVirtual call
base Method call
base MethodVirtual call
base Method call
TestShape End
TestDerive Start
circle override MethodVirtual
base Method call
base MethodVirtual call
base Method call
triangle new MethodVirtual
triangle new Method
Diamond default MethodVirtual
Diamond default Method
TestShape End
TestDerive2 Start
circle override MethodVirtual
base Method call
base MethodVirtual call
base Method call
base MethodVirtual call
base Method call
base MethodVirtual call
base Method call
TestDerive2 End
四:某個同學的比較差的記錄
視頻部分非公開,請聯系最課程(www.zuikc.com)。
微信掃一掃,關注最課程(www.zuikc.com),獲取更多我的文章,獲取軟件開發每日一練
新聞熱點
疑難解答