1. 多態性定義 C#中的多態性是OOP(面向對象編程)的一個基本概念,它允許一個對象在不同情況下表現出不同的行為,以增強代碼的可重用性和靈活性。 根據網上的教程,我們得知C#多態性分為兩類,靜態和動態。但實際上,C#沒有嚴格的靜態和動態多態性的分法。之所以這麼分,還是為了我們便於理解,我們沿用這個 ...
1. 多態性定義
C#中的多態性是OOP(面向對象編程)的一個基本概念,它允許一個對象在不同情況下表現出不同的行為,以增強代碼的可重用性和靈活性。
根據網上的教程,我們得知C#多態性分為兩類,靜態和動態。但實際上,C#沒有嚴格的靜態和動態多態性的分法。之所以這麼分,還是為了我們便於理解,我們沿用這個思維來大概分類:
採用函數重載或運算符重載方法的,屬於靜態多態性;
採用虛方法、抽象方法、介面等方式,屬於動態多態性。
拓展:在靜態多態性中,函數的響應是在編譯時發生的。在動態多態性中,函數的響應是在運行時發生的。什麼意思呢?
在靜態語言中,許多多態性的特性可以在編譯時確定,編譯器可以根據數據類型的信息來確定方法的調用方式。
而在動態語言中,數據類型的確定通常是在運行時進行的,這種行為被稱為動態多態性。
2. 函數重載示例
函數重載是指在同一個類中,定義多個方法,它們的方法名相同,但是參數類型、參數數量、參數順序不同。以下是一個函數重載的例子:
public class Calculator
{
public int Add(int a, int b)
{
return a + b;
}
public int Add(int a, int b, int c)
{
return a + b + c;
}
public float Add(float a, float b)
{
return a + b;
}
public double Add(double a, double b)
{
return a + b;
}
}
在這個例子中,Calculator類定義了4個Add()方法,它們的方法名相同但是參數列表不同(分別有2個整型參數、3個整型參數、2個浮點型參數、2個雙精度浮點型參數)。這些方法根據聲明的參數類型和數量而得到不同的簽名,因此構成函數重載,當調用Add()方法時,編譯器會根據參數的類型和數量來選擇正確的方法重載進行調用。
3. 虛方法示例
虛方法和重寫(override)方法:在父類中聲明一個虛方法,子類可以重寫該方法並實現自己的行為。在運行時,程式根據對象的實際類型調用相應的方法。這種方式也稱為“消除靜態綁定”。
註意事項:虛方法是使用關鍵字 virtual 聲明的。同時繼承類中的重寫虛函數需要聲明關鍵字 override 。下麵是一個示例:
// 定義一個Animal類和其子類
class Animal
{
public virtual void Speak()
{
Console.WriteLine("I am an animal.");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Woof!");
}
}
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine("Meow!");
}
}
// 示常式序
class Program
{
static void Main(string[] args)
{
Animal[] animals = new Animal[2];
animals[0] = new Dog();
animals[1] = new Cat();
foreach (Animal animal in animals)
{
animal.Speak();
}
Console.ReadKey();
}
}
在這個例子中,Animal類中聲明瞭一個虛方法Speak(),它的子類 Dog 和 Cat 分別對該方法進行了重寫。在Main 方法中,創建了一個 Animal 數組,其中存放了 Dog 和 Cat 的實例。在foreach迴圈中,程式根據實際對象類型調用了不同的Speak()方法,實現了多態性的效果。
小拓展:關鍵字重寫 override 與覆蓋 new 較為容易搞混,有關兩者區別可移步:C#中重寫(override)及覆蓋(new)的區別詳解。
4. 抽象方法示例
抽象方法是在抽象類中定義的,它沒有具體實現的代碼,而只是定義了方法的名稱、參數和返回值類型等信息。抽象方法必須在子類中進行完整的實現,否則子類本身也必須定義為抽象類。使用abstract關鍵字來定義抽象類和抽象方法。
下麵的示例演示瞭如何定義並使用抽象方法:
abstract class Shape
{
public abstract double Area(); // 定義抽象方法Area()
}
// 派生類Rectangle繼承抽象類Shape
class Rectangle : Shape
{
double width, height;
public Rectangle(double w, double h)
{
width = w;
height = h;
}
public override double Area() // 實現抽象方法Area()
{
return width * height;
}
}
class Triangle : Shape
{
double baseValue, height;
public Triangle(double bv, double h)
{
baseValue = bv;
height = h;
}
public override double Area() // 實現抽象方法Area()
{
return baseValue * height * 0.5;
}
}
class Program
{
static void Main(string[] args)
{
Rectangle r = new Rectangle(5, 8);
Console.WriteLine("矩形的面積 = {0}", r.Area());
Triangle t = new Triangle(5, 8);
Console.WriteLine("三角形的面積 = {0}", t.Area());
}
}
上面的代碼定義了Shape類和兩個派生類Rectangle和Triangle。Shape類中定義了一個抽象方法Area(),併在Rectangle和Triangle中實現了這個抽象方法。在Main方法中,創建了一個Rectangle對象 r 和一個Triangle對象 t,並分別調用它們的Area()方法計算出它們的面積。
5. 介面示例
C#介面是一種約定,是一個抽象的類型,它定義了一組公共的方法、屬性、索引器和事件,這些成員沒有實現細節和實現代碼,只定義了介面的行為。
5.1 介面語法
定義介面的語法如下:
interface 介面名稱
{
方法1
方法2
屬性1
索引器1
事件1
}
其中,方法、屬性、索引器和事件都是介面的成員,它們都沒有實現,只是定義了行為的名稱和參數。
方法定義的語法如下:
返回類型 方法名稱(參數列表);
屬性定義的語法如下:
屬性類型 屬性名稱 { get; set; }
索引器定義的語法如下:
索引器類型 this[索引器參數] { get; set; }
事件定義的語法如下:
event 事件委托類型 事件名稱;
其中,事件委托類型是一個Delegate類型。
5.2 介面使用示例
定義一個介面之後,可以通過繼承或實現來使用介面。介面的繼承使用“:”符號,需要註意的是,如果一個類實現了一個介面,那麼它必須實現介面中所有的方法和屬性。下麵是介面示例:
interface IShape
{
double Perimeter();
double Area();
}
interface ICircle : IShape
{
double Radius { get; set; }
}
interface IRectangle : IShape
{
double Width { get; set; }
double Height { get; set; }
}
class Circle : ICircle
{
public double Radius { get; set; }
public double Perimeter()
{
return 2 * Math.PI * Radius;
}
public double Area()
{
return Math.PI * Radius * Radius;
}
}
class Rectangle : IRectangle
{
public double Width { get; set; }
public double Height { get; set; }
public double Perimeter()
{
return 2 * (Width + Height);
}
public double Area()
{
return Width * Height;
}
}
class Program
{
static void Main(string[] args)
{
Circle c = new Circle();
c.Radius = 1;
Console.WriteLine("Circle: Perimeter = {0}, Area = {1}", c.Perimeter(), c.Area());
Rectangle r = new Rectangle();
r.Width = 2;
r.Height = 3;
Console.WriteLine("Rectangle: Perimeter = {0}, Area = {1}", r.Perimeter(), r.Area());
}
}
上面的代碼定義了IShape、ICircle和IRectangle三個介面、以及Circle和Rectangle兩個類。其中,Circle類實現了ICircle介面,Rectangle類實現了IRectangle介面。在Main方法中,創建了一個Circle對象和一個Rectangle對象,並給它們的屬性賦值。然後分別調用了Circle和Rectangle對象的Perimeter()和Area()方法輸出結果。
運行程式,輸出以下結果:
Circle: Perimeter = 6.28318530717959, Area = 3.14159265358979
Rectangle: Perimeter = 10, Area = 6
可以看到,通過使用介面,我們可以很方便地定義出不同的形狀,然後計算出它們的周長和麵積。這就展示了介面在C#編程中的重要地位。
想必大家看到這裡對於多態性的實現方式已經有了一定的瞭解,不知大家是否發現,這些方式中,抽象方法和虛方法是如此的相似,那麼我們就有了新的疑問:兩者有何區別?使用場景是否有所不同?
在下麵這篇隨筆中,我針對這個問題進行了深入學習:C#中抽象方法與虛方法的區別。