類的繼承與派生(virtual、override)

C#中類的繼承與派生在性質上類似C++,但在有些方面有一些區別。

C#中所有類都派生自object類

除了特殊的類object,所有的類都是派生類,即使他們沒有基類規格說明,類object是唯一的非派生類,因爲他是繼承層次結構的基礎,與C++不同(C++中允許多繼承),在C#中一個類聲明的基類規格說明中只能有一個單獨的類,也就是隻允許單繼承,雖然類只能直接繼承一個基類,但繼承的層次沒有限制。在寫法上也與C++不同,C#中不存在公有繼承、私有繼承等這些繼承方式,所以在寫的時候冒號後面直接接上基類的名字即可,例如 class A : B

屏蔽基類成員

雖然子類不能刪除它繼承的任何成員,但可以用與父類成員名稱相同的成員來屏蔽(mask)父類成員,這是繼承的主要功能之一。

屏蔽的使用:
(1)屏蔽一個繼承的數據成員,需要聲明一個新的相同類型的成員,並使用相同名稱
(2)屏蔽一個繼承的函數成員,需要聲明一個帶有相同簽名的函數成員,簽名由名稱和參數列表組成,不包括返回類型
(3)要讓編譯器知道你在故意屏蔽繼承的成員,可以使用new修飾符,否則,程序可以成功編譯,但編譯器會警告你隱藏了一個繼承的成員
(4)也可以屏蔽靜態成員

    class A
    {
        public int i;
        public void foo(){ }
    }

    class B :A 
    {
        new public int i;
        public void foo() { }  //不加new會有一個警告

    }

基類訪問

表達式:base.成員

虛方法(virtual)和覆寫方法(override)

類似C++中的多態的產生,C++中使用子類對象給父類指針賦值,當該父類指針調用虛函數時,就會產生多態,同理C#中使用子類對象的棧上引用給父類引用賦值,當父類引用調用虛方法時,就會產生多態。

    class A
    {
        public virtual void foo()
        {
            Console.WriteLine("父類");
        }
    }

    class B :A 
    {
        public override void foo()
        {
            Console.WriteLine("子類");
        }  
    }

    class Program
    {
        static void Main(string[] args)
        {
            B b = new B();
            A a = (A)b;
            a.foo();  //輸出子類
        }
    }

virtual和override修飾符的使用

父類中被覆寫的方法用virtual來修飾,子類中覆寫後的方法需要用override來修飾

(1)覆寫和被覆寫的方法必須有相同的可訪問性
(2)不能覆寫static方法或非虛方法
(3)方法、屬性、索引器以及事件,都可以被聲明爲virtual和override

virtual與override的內部調用機制

當使用對象基類部分的引用調用一個覆寫的方法時,方法的調用被沿派生層次上溯執行,取尋找標記爲override的方法,如果在更高的派生級別有該方法的其他聲明,但沒有被標記爲override,那麼它們不會被調用

  class A
    {
        public virtual void foo()
        {
            Console.WriteLine("A");
        }
    }

    class B :A 
    {
        public override void foo()
        {
            Console.WriteLine("B");
        }  
    }

    class C:B
    {
        public override void foo()
        {
            Console.WriteLine("C");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            C c = new C();
            A a = (A)c;
            a.foo();    //輸出C
        }
    }

此時a調用foo函數,會根據繼承關係向上尋找override方法,在C中有此方法則會調用到C中的foo所以打印出C

    class C:B
    {
        public new void foo()
        {
            Console.WriteLine("C");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            C c = new C();
            A a = (A)c;
            a.foo();    //輸出B
        }
    }

如果將C中的foo函數使用new來修飾,而不是override,則在調用時找不到C中的override修飾的foo函數,則只能調用B中被override修飾的foo函數所以輸出爲B

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章