Visual Studio 2010
其他版本
Visual Studio 2008
Visual Studio 2005
多態性常被視為自封裝和繼承之後,面向對象的編程的第三個支柱。Polymorphism(多態性)是一個希臘詞,指“多種形態”,多態性具有兩個截然不同的方面:
在運行時,在方法參數和集合或數組等位置,派生類的對象可以作為基類的對象處理。發生此情況時,該對象的聲明類型不再與運行時類型相同。
基類可以定義並實現虛方法,派生類可以重寫這些方法,即派生類提供自己的定義和實現。在運行時,客戶端代碼調用該方法,CLR 查找對象的運行時類型,並調用虛方法的重寫方法。因此,您可以在源代碼中調用基類的方法,但執行該方法的派生類版本。
虛方法允許您以統一方式處理多組相關的對象。例如,假定您有一個繪圖應用程序,允許用戶在繪圖圖面上創建各種形狀。您在編譯時不知道用戶將創建哪些特定類型的形狀。但應用程序必須跟蹤創建的所有類型的形狀,並且必須更新這些形狀以響應用戶鼠標操作。您可以使用多態性通過兩個基本步驟解決這一問題:
創建一個類層次結構,其中每個特定形狀類均派生自一個公共基類。
使用虛方法通過對基類方法的單個調用來調用任何派生類上的相應方法。
首先,創建一個名為 Shape 的基類,並創建一些派生類,例如 Rectangle、Circle 和 Triangle。為 Shape 類提供一個名為 Draw 的虛方法,並在每個派生類中重寫該方法以繪制該類表示的特定形狀。創建一個 List<Shape> 對象,並向該對象添加 Circle、Triangle 和 Rectangle。若要更新繪圖圖面,請使用 foreach 循環對該列表進行循環訪問,並對其中的每個 Shape 對象調用 Draw 方法。雖然列表中的每個對象都具有聲明類型 Shape,但調用的將是運行時類型(該方法在每個派生類中的重寫版本)。
VB
C#
C++
F#
JScript
復制
public class Shape
{
// A few example members
public int X { get; private set; }
public int Y { get; private set; }
public int Height { get; set; }
public int Width { get; set; }
// Virtual method
public virtual void Draw()
{
Console.WriteLine("Performing base class drawing tasks");
}
}
class Circle : Shape
{
public override void Draw()
{
// Code to draw a circle...
Console.WriteLine("Drawing a circle");
base.Draw();
}
}
class Rectangle : Shape
{
public override void Draw()
{
// Code to draw a rectangle...
Console.WriteLine("Drawing a rectangle");
base.Draw();
}
}
class Triangle : Shape
{
public override void Draw()
{
// Code to draw a triangle...
Console.WriteLine("Drawing a triangle");
base.Draw();
}
}
class Program
{
static void Main(string[] args)
{
// Polymorphism at work #1: a Rectangle, Triangle and Circle
// can all be used whereever a Shape is expected. No cast is
// required because an implicit conversion exists from a derived
// class to its base class.
System.Collections.Generic.List<Shape> shapes = new System.Collections.Generic.List<Shape>();
shapes.Add(new Rectangle());
shapes.Add(new Triangle());
shapes.Add(new Circle());
// Polymorphism at work #2: the virtual method Draw is
// invoked on each of the derived classes, not the base class.
foreach (Shape s in shapes)
{
s.Draw();
}
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* Output:
Drawing a rectangle
Performing base class drawing tasks
Drawing a triangle
Performing base class drawing tasks
Drawing a circle
Performing base class drawing tasks
*/
在 C# 中,每個類型都是多態的,因為包括用戶定義類型在內的所有類型都繼承自 Object。
多態性概述
虛成員
當派生類從基類繼承時,它會獲得基類的所有方法、字段、屬性和事件。派生類的設計器可以選擇是否
重寫基類中的虛擬成員。
繼承最接近的基類方法而不重寫它
定義隱藏基類實現的成員的新非虛實現
僅當基類成員聲明為 virtual 或 abstract 時,派生類才能重寫基類成員。派生成員必須使用 override 關鍵字顯式指示該方法將參與虛調用。以下代碼提供了一個示例:
VB
C#
C++
F#
JScript
復制
public class BaseClass
{
public virtual void DoWork() { }
public virtual int WorkProperty
{
get { return 0; }
}
}
public class DerivedClass : BaseClass
{
public override void DoWork() { }
public override int WorkProperty
{
get { return 0; }
}
}
字段不能是虛擬的,只有方法、屬性、事件和索引器才可以是虛擬的。當派生類重寫某個虛擬成員時,即使該派生類的實例被當作基類的實例訪問,也會調用該成員。以下代碼提供了一個示例:
VB
C#
C++
F#
JScript
復制
DerivedClass B = new DerivedClass();
B.DoWork(); // Calls the new method.
BaseClass A = (BaseClass)B;
A.DoWork(); // Also calls the new method.
虛方法和屬性允許派生類擴展基類,而無需使用方法的基類實現。有關更多信息,請參見 使用 Override 和 New 關鍵字進行版本控制(C# 編程指南)。接口提供另一種方式來定義將實現留給派生類的方法或方法集。有關更多信息,請參見接口(C# 編程指南)和在類和接口之間選擇。
使用新成員隱藏基類成員
如果希望派生成員具有與基類中的成員相同的名稱,但又不希望派生成員參與虛調用,則可以使用 new 關鍵字。new 關鍵字放置在要替換的類成員的返回類型之前。以下代碼提供了一個示例:
VB
C#
C++
F#
JScript
復制
public class BaseClass
{
public void DoWork() { WorkField++; }
public int WorkField;
public int WorkProperty
{
get { return 0; }
}
}
public class DerivedClass : BaseClass
{
public new void DoWork() { WorkField++; }
public new int WorkField;
public new int WorkProperty
{
get { return 0; }
}
}
通過將派生類的實例強制轉換為基類的實例,仍然可以從客戶端代碼訪問隱藏的基類成員。例如:
VB
C#
C++
F#
JScript
復制
DerivedClass B = new DerivedClass();
B.DoWork(); // Calls the new method.
BaseClass A = (BaseClass)B;
A.DoWork(); // Calls the old method.
阻止派生類重寫虛擬成員
無論在虛擬成員和最初聲明虛擬成員的類之間已聲明了多少個類,虛擬成員永遠都是虛擬的。如果類 A 聲明了一個虛擬成員,類 B 從 A 派生,類 C 從類 B 派生,則類 C 繼承該虛擬成員,並且可以選擇重寫它,而不管類 B 是否為該成員聲明了重寫。以下代碼提供了一個示例:
VB
C#
C++
F#
JScript
復制
public class A
{
public virtual void DoWork() { }
}
public class B : A
{
public override void DoWork() { }
}
派生類可以通過將重寫聲明為 sealed 來停止虛擬繼承。這需要在類成員聲明中的 override 關鍵字前面放置 sealed 關鍵字。以下代碼提供了一個示例:
VB
C#
C++
F#
JScript
復制
public class C : B
{
public sealed override void DoWork() { }
}
在上一個示例中,方法 DoWork 對從 C 派生的任何類都不再是虛擬方法。即使它們轉換為類型 B 或類型 A,它對於 C 的實例仍然是虛擬的。通過使用 new 關鍵字,密封的方法可以由派生類替換,如下面的示例所示:
VB
C#
C++
F#
JScript
復制
public class D : C
{
public new void DoWork() { }
}
在此情況下,如果在 D 中使用類型為 D 的變量調用 DoWork,被調用的將是新的 DoWork。如果使用類型為 C、B 或 A 的變量訪問 D 的實例,對 DoWork 的調用將遵循虛擬繼承的規則,即把這些調用傳送到類 C 的 DoWork 實現。
從派生類訪問基類虛擬成員
已替換或重寫某個方法或屬性的派生類仍然可以使用基關鍵字訪問基類的該方法或屬性。以下代碼提供了一個示例:
VB
C#
C++
F#
JScript
復制
public class Base
{
public virtual void DoWork() {/*...*/ }
}
public class Derived : Base
{
public override void DoWork()
{
//Perform Derived's work here
//...
// Call DoWork on base class
base.DoWork();
}
}
作者“計算機小百科(僅用於技..”