前言
本文主要來講解一下C#中,自己覺得掌握的不怎么樣或者用的不多,不太熟悉的關鍵字,主要包括base、this、new、override、abstract、virtual以及針對static字段和static構造函數之間的執行問題。
base關鍵字
base 關鍵字用於在派生類中實現對基類公有或者受保護成員的訪問,但是只局限在構造函數、實例方法和實例屬性訪問器中:
-
調用基類上已被其他方法重寫的方法。
public class Father { public virtual void Say() { Console.WriteLine("Father Say"); } } public class Son : Father { public override void Say() { base.Say(); Console.WriteLine("Son Say"); } }
-
指定創建派生類實例時應調用的基類構造函數。
public class Father { public string Name { get; set; } public Father() { Name = "Father"; } } public class Son : Father { public Son() : base() { } }
從靜態方法中使用 base 關鍵字是錯誤的。
this關鍵字
其用於引用類的當前實例,也包括繼承而來的方法,通常可以隱藏this:
- 限定被相似的名稱隱藏的成員
public class Person { public string Name { get; set; } public int Age { get; set; } public Person(string Name, int Age) { this.Name = Name; this.Age = Age; } }
- 將對象作為參數傳遞到其他方法
public class Person { public string Name { get; set; } public int Age { get; set; } public Person(string Name, int Age) { this.Name = Name; this.Age = Age; } public void CallTest(Person person) { Console.WriteLine(person.Name+person.Age); } public void Call() { CallTest(this); } }
- 聲明索引器
public class Person { string[] PersonList = new string[10]; public string this[int param] { get { return PersonList[param]; } set { PersonList[param] = value; } }
new關鍵字
一、new運算符
1、new一個class時,new完成了以下兩個方面的內容:一是調用new class命令來為實例在托管堆中分配內存;二是調用構造函數來實現對象初始化。
2、new一個struct時,new運算符用於調用其帶構造函數,完成實例的初始化。
3、new一個int時,new運算符用於初始化其值為0。
4、 new運算符不可重載。
5、new分配內存失敗,將引發OutOfMemoryException異常。
二、new修飾符
new 關鍵字可以顯式隱藏從基類繼承的成員。 隱藏繼承的成員時,該成員的派生版本將替換基類版本。 雖然可以在不使用 new 修飾符的情況下隱藏成員,但會生成警告。 如果使
用 new 顯式隱藏成員,則會取消此警告,並記錄要替換為派生版本這一事實。
在子類中用 new 關鍵字修飾 定義的與父類中同名的方法,叫覆蓋。 覆蓋不會改變父類方法的功能。
public class A { public virtual void Test() { Console.WriteLine("A.Test()"); } } public class B : A { public new void Test() { Console.WriteLine("B.Test()"); } } class Program { static void Main(string[] args) { A a = new A(); a.Test(); B b = new B(); b.Test(); A c = new B(); c.Test(); Console.ReadLine(); } } }
當用子類創建父類的時候,如 A c = new B(),覆蓋不會改變父類的功能,仍然調用父類功能。(和override有區別,下面進行講解)
三、new 約束
new 約束指定泛型類聲明中的任何類型參數都必須有公共的無參數構造函數。 如果要使用 new 約束,則該類型不能為抽象類型。
當與其他約束一起使用時,new() 約束必須最后指定:
public class ClassA<T>where T : IComparable, new() { //// }
override關鍵字
要擴展或修改繼承的方法、屬性、索引器或事件的抽象實現或虛實現,必須使用 override 修飾符。
由 override 聲明重寫的方法稱為重寫基方法。 重寫的基方法必須與 override 方法具有相同的簽名。
不能重寫非虛方法或靜態方法。 重寫的基方法必須是 virtual、abstract 或 override 的。
用關鍵字 virtual 修飾的方法,叫虛方法。可以在子類中用override 聲明同名的方法,這叫“重寫”。相應的沒有用virtual修飾的方法,我們叫它實方法。 重寫會改變父類方法的功能。
public class A { public virtual void Test() { Console.WriteLine("A.Test()"); } } public class B : A { public override void Test() { Console.WriteLine("B.Test()"); } } class Program { static void Main(string[] args) { A a = new A(); a.Test(); B b = new B(); b.Test(); A c = new B(); c.Test(); Console.ReadLine(); } }
new 和override
1、 不管是重寫還是覆蓋都不會影響父類自身的功能。
2、當用子類創建父類的時候,如 A c = new B(),重寫會改變父類的功能,即調用子類的功能;而覆蓋不會,仍然調用父類功能。
3、虛方法、實方法都可以被覆蓋(new),抽象方法,接口 不可以。
4、抽象方法,接口,標記為virtual的方法可以被重寫(override),實方法不可以。
5、重寫使用的頻率比較高,實現多態;覆蓋用的頻率比較低,用於對以前無法修改的類進行繼承的時候。
abstract關鍵字
針對abstract關鍵字暫時總結了以下五點:
1.用關鍵字abstract定義的類即為抽象類,且只能作為基類,也不能被實例化。
2.用abstract定義的類不一定包含抽象方法,也可以包含非抽象方法。
3.abstract定義的方法一定包含在抽象類中。
4.抽象類不能定義為密封類(sealed),抽象方法不能使用virtual、static、private修飾符
5.如果派生類沒有實現所有的抽象方法,則該派生類也必須聲明為抽象類。
virtual關鍵字
Virtual方法(虛方法)
virtual 關鍵字用於在基類中修飾方法。virtual的使用會有兩種情況:
情況1:在基類中定義了virtual方法,但在派生類中沒有重寫該虛方法。那么在對派生類實例的調用中,該虛方法使用的是基類定義的方法。
情況2:在基類中定義了virtual方法,然后在派生類中使用override重寫該方法。那么在對派生類實例的調用中,該虛方法使用的是派生重寫的方法。
static字段和static構造函數
主要來說明執行的順序:
1、編譯器在編譯的時候,先分析所需要的靜態字段,如果這些靜態字段所在的類有靜態的構造函數,那么就會忽略字段的初始化;如果沒有靜態的構造函數,那么就會對靜態字段進行初始化。
2、如果存在多個靜態類,那么初始化的靜態成員的順序會根據引用的順序,先引用到的先進行初始化,但如果類的靜態成員的初始化依賴於其他類的靜態成員,則會先初始化被依賴的靜態成員。
3、而帶有靜態構造函數的類的靜態字段,只有在引用到的時候才進行初始化。
下面我們來看兩個簡單的小例子:
public class A { public static int X = B.Y+1; static A() { } } public class B { public static int Y=A.X+1; } class Program { static void Main(string[] args) { Console.WriteLine("A.X={0},B.Y={1}",A.X,B.Y); Console.ReadLine(); } }
結果如何呢?再來看第二個小例子:
public class A { public static int X = B.Y+1; } public class B { public static int Y=A.X+1; static B() { } } class Program { static void Main(string[] args) { Console.WriteLine("A.X={0},B.Y={1}",A.X,B.Y); Console.ReadLine(); } }
對比一下,這兩個例子,如果你想的和你執行后的結果一致,那說明你應該已經明白它們的執行順序了。