C# 多型性

衣舞晨風發表於2015-06-11

轉載自:MSDN

類似文章:點選開啟連結

多型性常被視為自封裝和繼承之後,物件導向的程式設計的第三個支柱。 Polymorphism(多型性)是一個希臘詞,指“多種形態”,多型性具有兩個截然不同的方面:

  • 在執行時,在方法引數和集合或陣列等位置,派生類的物件可以作為基類的物件處理。 發生此情況時,該物件的宣告型別不再與執行時型別相同。

  • 基類可以定義並實現方法,派生類可以重寫這些方法,即派生類提供自己的定義和實現。 在執行時,客戶端程式碼呼叫該方法,CLR 查詢物件的執行時型別,並呼叫虛方法的重寫方法。 因此,你可以在原始碼中呼叫基類的方法,但執行該方法的派生類版本。

  • 派生類(子類)與基類(父類)之間強制轉換不會丟失資訊。

  • namespace PolymorphismTest
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
            private void button1_Click(object sender, EventArgs e)
            {
                ParentClass parent = new ParentClass();
                SubClass sub = new SubClass();
                parent = ((ParentClass)sub);
                //子類 父類 例項之間來回轉換不會丟失資訊
               SubClass subNew = (SubClass)parent;
            }
        }
        public class SubClass : ParentClass
        {
            public new string name = "SubClass";
            public int age = 20;
        }
        public class ParentClass
        {
            string name = "ParentClass"; 
        }
    }
    執行到
    SubClass subNew = (SubClass)parent
    具體結果如下圖:


虛方法允許你以統一方式處理多組相關的物件。 例如,假定你有一個繪圖應用程式,允許使用者在繪圖圖面上建立各種形狀。 你在編譯時不知道使用者將建立哪些特定型別的形狀。 但應用程式必須跟蹤建立的所有型別的形狀,並且必須更新這些形狀以響應使用者滑鼠操作。 你可以使用多型性通過兩個基本步驟解決這一問題:

  1. 建立一個類層次結構,其中每個特定形狀類均派生自一個公共基類。

  2. 使用虛方法通過對基類方法的單個呼叫來呼叫任何派生類上的相應方法。

首先,建立一個名為 Shape 的基類,並建立一些派生類,例如 RectangleCircle 和 Triangle。 為 Shape 類提供一個名為 Draw 的虛方法,並在每個派生類中重寫該方法以繪製該類表示的特定形狀。建立一個 List<Shape> 物件,並向該物件新增 Circle、Triangle 和 Rectangle。 若要更新繪圖圖面,請使用 foreach 迴圈對該列表進行迴圈訪問,並對其中的每個 Shape 物件呼叫 Draw 方法。 雖然列表中的每個物件都具有宣告型別 Shape,但呼叫的將是執行時型別(該方法在每個派生類中的重寫版本)。

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 關鍵字顯式指示該方法將參與虛呼叫。 以下程式碼提供了一個示例:

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; }
    }
}

欄位不能是虛擬的,只有方法、屬性、事件和索引器才可以是虛擬的。 當派生類重寫某個虛擬成員時,即使該派生類的例項被當作基類的例項訪問,也會呼叫該成員。 以下程式碼提供了一個示例:

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 關鍵字放置在要替換的類成員的返回型別之前。 以下程式碼提供了一個示例:

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; }
    }
}

通過將派生類的例項強制轉換為基類的例項,仍然可以從客戶端程式碼訪問隱藏的基類成員。 例如:

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 是否為該成員宣告瞭重寫。 以下程式碼提供了一個示例:

public class A
{
    public virtual void DoWork() { }
}
public class B : A
{
    public override void DoWork() { }
}

派生類可以通過將重寫宣告為 sealed 來停止虛擬繼承。 這需要在類成員宣告中的 override 關鍵字前面放置 sealed 關鍵字。 以下程式碼提供了一個示例:

public class C : B
{
    public sealed override void DoWork() { }
}

在上一個示例中,方法 DoWork 對從 C 派生的任何類都不再是虛擬方法。 即使它們轉換為型別 B 或型別 A,它對於 C 的例項仍然是虛擬的。 通過使用 new 關鍵字,密封的方法可以由派生類替換,如下面的示例所示:

public class D : C
{
    public new void DoWork() { }
}

在此情況下,如果在 D 中使用型別為 D 的變數呼叫 DoWork,被呼叫的將是新的 DoWork。 如果使用型別為 C、B 或 A 的變數訪問 D 的例項,對 DoWork 的呼叫將遵循虛擬繼承的規則,即把這些呼叫傳送到類 C 的 DoWork 實現。

已替換或重寫某個方法或屬性的派生類仍然可以使用基關鍵字訪問基類的該方法或屬性。 以下程式碼提供了一個示例:

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();
    }
}

有關詳細資訊,請參閱 base

說明 說明

建議虛擬成員在它們自己的實現中使用 base 來呼叫該成員的基類實現。 允許基類行為發生使得派生類能夠集中精力實現特定於派生類的行為。 未呼叫基類實現時,由派生類負責使它們的行為與基類的行為相容。

相關文章