C# 程式設計/繼承
繼承的好處是什麼?
- 它為你節省了很多打字。
- 它可以避免你重複自己。
繼承的類比解釋
假設你想建立一個鷹、一隻獵鷹和一隻禿鷲。為了建立這些飛行動物,你注意到這些生物都有以下特點:
- 飛行
- 繁殖
- 進食
假設這三種類型的鳥類以完全相同的方式飛行、繁殖和進食。
如果沒有繼承,你將被迫複製程式碼。即,讓鷹飛行的相同程式碼也將被複制以使禿鷲飛翔。對於程式設計師來說,這是一個公理——他們是一群懶惰的人,不想重複自己——重複幾乎總是件壞事。
請注意,鷹、獵鷹和禿鷲實際上都是鳥類。因此,可以說鳥類通常總是有進食、繁殖和飛行的特徵。所以,使用“繼承”,你可以建立一個通用的“鳥類”原型,它可以進食、繁殖和飛行,然後定義好後,你可以讓所有其他特定的鳥類繼承這些特徵。換句話說,使用原型,你可以根據這個原型設計其他特定的鳥類。
這意味著獵鷹會自動知道如何飛行,因為它從通用“鳥類”類繼承了這種行為。你基本上不必重複自己。
繼承是能夠從另一個類(“父”類)建立類,擴充套件父類在派生類或“子”類中的功能和狀態的能力。它允許派生類過載來自其父類的函式。
繼承是面向物件程式設計的支柱之一。它是從另一個類設計一個類的機制,是程式碼可重用的理念之一,支援層次分類的概念。C# 程式由類組成,其中新的類可以從頭開始建立,也可以使用現有類的某些或所有屬性來建立。
與繼承和程式碼可重用性相關的另一個特性是多型性,它允許對不同資料型別進行不同操作時使用相同的函式名稱。因此,C# 透過這兩個特性支援程式碼可重用性。
繼承的重要特徵包括:
- 派生類擴充套件其基類。也就是說,它包含其父類的函式和資料,並且它還可以包含自己的資料成員和函式。
- 派生類不能更改繼承成員的定義。
- 建構函式和解構函式不會被繼承。基類的所有其他成員都會被繼承。
- 派生類中成員的可訪問性取決於其在基類中宣告的可訪問性。
- 派生類可以重寫繼承的成員。
繼承的示例
using System;
using System.Text;
namespace ContainmentInheritance
{
class Room
{
public int length;
public int width;
public int height;
public Room(int l, int w, int h)
{
length = l;
width = w;
height = h;
}
}
class Home
{
int numberOfRooms;
int plotSize;
string locality;
string name;
// create an object of class Room inside class Home
Room studyRoom = new Room(10, 12, 12);
public Home()
{
numberOfRooms = 1;
plotSize = 1000;
locality = "Versova";
name = "study room";
}
public void Display()
{
Console.WriteLine("MyHome has {0} rooms", numberOfRooms);
Console.WriteLine("Plot size is {0}", plotSize);
Console.WriteLine("Locality is {0}", locality);
int area = studyRoom.length*studyRoom.width;
Console.WriteLine("Area of the {0} room is {1}", name, area);
}
}
class Program
{
static void Main(string[] args)
{
Home myhome = new Home();
myhome.Display();
Console.ReadLine();
}
}
}
下面的程式碼示例展示了兩個類,Employee 和 Executive。 Employee 具有 GetPayCheck 和 Work 函式。
我們希望 Executive 類擁有相同的函式,但實現方式不同,並且有一個額外的函式,AdministerEmployee。
以下是第一個要派生的類的建立。
public class Employee
{
// We declare one method virtual so that the Executive class can
// override it.
public virtual void GetPayCheck()
{
// Get paycheck logic here.
}
//Employee's and Executives both work, so no virtual here needed.
public void Work()
{
// Do work logic here.
}
}
現在,我們建立一個 Executive 類,它將覆蓋 GetPayCheck 函式
public class Executive : Employee
{
// The override keyword indicates we want new logic behind the GetPayCheck method.
public override void GetPayCheck()
{
// New getpaycheck logic here.
}
// The extra method is implemented.
public void AdministerEmployee()
{
// Manage employee logic here
}
}
你會注意到,Executive 類中沒有 Work 函式,因為它繼承自 Employee。
static void Main(string[] args)
{
Employee emp = new Employee();
Executive exec = new Executive();
emp.Work();
exec.Work();
emp.GetPayCheck();
exec.GetPayCheck();
exec.AdministerEmployee();
}
如果基類包含一個虛方法,它在其他地方呼叫該方法,並且派生類覆蓋了該虛方法,那麼基類實際上會呼叫派生類的函式。
public class Resource : IDisposable
{
private bool _isClosed = false; // good programming practice initialise, although default
protected virtual void Close()
{
Console.WriteLine("Base resource closer called!");
}
~Resource()
{
Dispose();
}
public void Dispose()
{
if (!_isClosed)
{
Console.WriteLine("Disposing resource and calling the Close() method...");
_isClosed = true;
Close();
}
}
}
public class AnotherTypeOfResource : Resource
{
protected override void Close()
{
Console.WriteLine("Another type of resource closer called!");
}
}
public class VirtualMethodDemo
{
public static void Main()
{
Resource res = new Resource();
AnotherTypeOfResource res2 = new AnotherTypeOfResource();
res.Dispose(); // Resource.Close() will be called.
res2.Dispose(); // Even though Dispose() is part of the Resource class,
// the Resource class will call AnotherTypeOfResource.Close()!
}
}
派生類不會自動繼承基類的建構函式,並且它無法例項化,除非它提供自己的建構函式。派生類必須使用 base 關鍵字呼叫其基類的建構函式之一。
public class MyBaseClass
{
public MyBaseClass(string text)
{
console.WriteLine(text);
}
}
public class MyDerivedClass : MyBaseClass
{
public MyDerivedClass(int number)
: base(number.ToString())
{ }
public MyDerivedClass(string text) // even though this is exactly the same as MyBaseClass'
// only constructor, this is still necessary as constructors are not inherited.
: base(text)
{ }
}
C# 從另一個類繼承的語法方法是使用 : 運算子。
示例
public class Executive : Employee
要指示可以重寫的函式,請使用 virtual 標記函式。
public virtual void Write(string text)
{
System.Console.WriteLine("Text:{0}", text);
}
要覆蓋一個函式,請使用 override 關鍵字
public override void Write(string text)
{
System.Console.WriteLine(text);
}
派生函式中缺少 new 或 override 關鍵字可能會導致編譯期間出現錯誤或警告:[1] 以下是一個示例
abstract class ShapesA
{
abstract public int Area(); // abstract!
}
class Square : ShapesA
{
int x, y;
public int Area() // Error: missing 'override' or 'new'
{
return x * y;
}
}
class Shapes
{
virtual public int Area() { return 0; } // it is virtual now!
}
class Square : Shapes
{
int x, y;
public int Area() // no explicit 'override' or 'new' required
{ return x * y; }
}
Square 類函式 Area() 將導致編譯錯誤,如果它從 ShapesA 類派生
error CS0534: 'ConsoleApplication3.Square' does not implement inherited abstract member 'ConsoleApplication3.Shapes.Area()'
如果從普通的 Shapes 類派生,則相同的函式會導致編譯警告
warning CS0114: 'ConsoleApplication3.Square.Area()' hides inherited member 'ConsoleApplication3.Shapes.Area()'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
- ↑ Greg Beech (2010-03-09). "C# 設計:為什麼抽象方法需要 new/override,而虛擬方法不需要? / 答案". http://efreedom.com/: eFreedom. 檢索於 2011-08-11.
使用 .NET 3.5 SP1 中附帶的 C# 3.0 編譯器或 .NET 4.0 中附帶的 C# 4.0 編譯器,我都會在你的第一個例子中收到以下錯誤:[...],在第二個例子中收到以下警告:[...]。在第一個例子中,這是一個錯誤,因為你實際上並沒有重寫基方法,這意味著在具體類中沒有抽象方法的實現。在第二個例子中,這是一個警告,因為程式碼在技術上是正確的,但編譯器懷疑這不是你想要的。這是通常啟用“將警告視為錯誤”編譯設定的一個原因。
{{cite web}}: 外部連結在(幫助)|location=