其實我們對繼承並不陌生:
觀察上述代碼,我們可以看到:在我們並未給Program類聲明任何成員時,Program實例卻可以通過"."操作符呼出調用列表?這是因為:我們聲明的類默認繼承了Object類型,它是.NET所有數據類型的基類型
如何證明Program類型的基類型是Object?
第一種方式:我們可以在ToString()方法上右鍵,選擇"轉到定義"或者直接在ToString()方法上按下"F12"鍵
觀察上述代碼,我們可以看到:ToString()方法,確實定義在了Object類中;由於Program類默認繼承了Object類,所以Program實例也就擁有了ToString()這個公開的函數成員。
第二種方式:我們可以通過typeof操作符來獲取Program的基類型
class Program
{
static void Main(string[] args)
{
Type type = typeof(Program);
var baseType = type.BaseType;
Console.WriteLine(baseType.FullName);
Console.ReadLine();
}
}
/*輸出:System.Object*/
觀察上述代碼,我們也可以得出Program的基類型是Object
什么是繼承?
我想單單通過對繼承一通描述,並不能直觀的感受繼承是什么東西,下面我們通過代碼示例來認識繼承。
1) 我們需要定義一個長方形和圓形,並且通過ShowInfo方法獲取他們的面積和顏色。在不使用繼承的情況下我們會這樣實現:
namespace ExtendsDemo
{
class Program
{
static void Main(string[] args)
{
Square square = new Square(32.3,"紅色");
Circle circle = new Circle(33.4,"藍色");
square.ShowInfo();
circle.ShowInfo();
Console.ReadLine();
}
}
}
//先聲明一個 長方形類
class Square
{
public double SquareArea { get; set; }
public string SquareColor { get; set; }
public Square(double area,string color)
{
this.SquareArea = area;
this.SquareColor = color;
}
public void ShowInfo()
{
Console.WriteLine($"面積:{this.SquareArea},顏色:{this.SquareColor}");
}
}
//然后再聲明一個
class Circle
{
public double CircleArea { get; set; }
public string CircleColor { get; set; }
public Circle(double area, string color)
{
this.CircleArea = area;
this.CircleColor = color;
}
public void ShowInfo()
{
Console.WriteLine($"面積:{this.CircleArea},顏色:{this.CircleColor}");
}
}
/* 輸出:
面積:32.3,顏色:紅色
面積:33.4,顏色:藍色
*/
觀察上面代碼,我們可以看到:Square和Circle類的相似度很高,假如說我們此時還需要一個三角形並需要得知它的面積和顏色,那么我們可能就需要"Copy"、"Paste"代碼來實現了。而這種解決方式肯定是不可取的。
2) 當我們面對相似度很高的代碼片段時,第一反應就是要使用繼承:通過定義一個基類型將相似的代碼片段集中放置到基類型中以達到代碼復用的目的。
namespace ExtendsDemo
{
class Program
{
static void Main(string[] args)
{
Square square = new Square(32.3,"紅色");
Circle circle = new Circle(33.4,"藍色");
square.ShowInfo();
circle.ShowInfo();
Console.ReadLine();
}
}
}
class Shape
{
public double Area { get; set; }
public string Color { get; set; }
public Shape(double area)
{
}
public Shape(double area,string color)
{
this.Area = area;
this.Color = color;
}
public void ShowInfo()
{
Console.WriteLine($"面積:{this.Area},顏色:{this.Color}");
}
}
//在聲明類型的后面添加":"和類型(作為基類型),便完成了類的繼承
class Square : Shape
{
public Square(double area, string color) : base(area)
{
}
}
class Circle : Shape
{
public Circle(double area,string color):base(area,color)
{
}
}
我們通過改寫代碼,定義了一個Shape類型作為基類型,然后讓Squre和Cirlcle繼承Shape類,便完成了對代碼的重構,當我們需要一個Trangle形狀時,便可以在聲明類的時候繼承Shape類,快速應對需求的變動。
由以上面的代碼為例,我們來看以下問題:
1) 當子類繼承父類時,子類究竟從父類中繼承到了什么?
子類繼承了父類的數據成員和函數成員(構造函數除外)--這一點從上述Square和Circle類中除構造函數外沒有寫聲明任何成員,卻能調用ShowInfo()方法可以得知。
2) 當子類實例化時,為什么父類構造函數也會調用?
- 當子類實例化時,若父類沒有顯示提供任何構造函數,那么子類會默認隱式調用父類的默認構造函數:也就是說父類構造函數會先調用。
- 若父類沒有默認的無參構造函數,C#編譯器不會幫我們隱式調用基類無參構造函數了;但是我們還必須得調用基類的構造函數,那么子類就需要使用base上下文關鍵字來指定調用基類中的哪一個構造函數。
- 到這里,我們可以引用微軟官方文檔中對base的定義:base關鍵字用於從派生類中訪問基類成員
- 從上面兩點可以看出,子類並不能繼承父類的構造函數;換言之構造函數不能被繼承。--如果說構造函數能夠被繼承,那么何必費心在子類中調用基類構造函數呢。
3)創建子類實例時,有沒有創建基類實例?
- 首先答案是:沒有;我們只說過當子類型實例創建時,會先調用父類的構造函數;並沒有說會創建父類實例;
- 其次,我們可以在父類構造函數中和子類構造函數中打印一下HasCode,從打印結果證明new一個子類對象,並不會創建基類對象。
namespace ExtendsDemo
{
class Program
{
static void Main(string[] args)
{
Square square = new Square(32.3, "紅色");
square.ShowInfo();
Console.ReadLine();
}
}
}
//先聲明一個 長方形類
class Shape
{
public double Area { get; set; }
public string Color { get; set; }
public Shape(double area, string color)
{
this.Area = area;
this.Color = color;
Console.WriteLine(this.GetHashCode());
}
public void ShowInfo()
{
Console.WriteLine($"面積:{this.Area},顏色:{this.Color}");
}
}
class Square : Shape
{
public Square(double area, string color) : base(area, color)
{
Console.WriteLine(base.GetHashCode() + "---" + this.GetHashCode());
}
}
/* 輸出:
46104728
46104728---46104728
面積:32.3,顏色:紅色
*/
4) base就是充當一個調用基類成員的角色;它不能單獨輸出。
5)下面的代碼中base.Area和this.Area的base和this灰掉,說明了在當前情況下使用base和this沒有區別:他們都指向的內存中同一個地方。
那什么情況下base和this才有區別呢?這個在講多態的時候再介紹。
前面所講的例子,都是在幫助我們理解類的繼承的一些基本概念,其實類的繼承還有很多應用沒有講到,比如說:在子類中添加父類沒有的成員,這一操作我們稱為類成員的橫向擴展;
class Shape
{
public double Area { get; set; }
public string Color { get; set; }
public Shape(double area, string color)
{
this.Area = area;
this.Color = color;
Console.WriteLine(this.GetHashCode());
}
public void ShowInfo()
{
Console.WriteLine($"面積:{this.Area},顏色:{this.Color}");
}
}
class Square : Shape
{
//子類成員越來越多了,相對於基類看起來像是長胖了--橫向擴展
public string Foreground { get; set; }
public Square(double area, string color) : base(area, color)
{
Console.WriteLine(base.Area + "---" + this.Area);
}
}
以上便是對類的繼承的簡要總結,記錄下來以便以后查閱。