C#中的接口
在C#中接口是一組公共方法或屬性的集合。接口可以被其他接口或是類繼承,但不能被實例化。
1、接口中包含的屬性和方法都是公共的,不是繼承或是私有的。事實上,在C#中定義接口中的成員時,不允許顯示指定接口成員的可訪問性,而是自動默認為公共的。
2、接口中只能包含普通方法或屬性,而不能包含其他內容,如構造函數、變量等。
3、當接口被某個類繼承時,通常說類實現了這個接口,而較少說類繼承了接口。
4、接口中的方法和屬性只有簽名部分,而沒有實現部分,甚至連接口名後面的大括號也不能有,否則會出現編譯錯誤。
在C#中用interface關鍵字定義一個接口
訪問修飾符 interface 接口名
{
//接口成員(方法和屬性)
}
C#中約定接口的名字以字母I開頭。如以下代碼定義一個接口IPerson
1 public interface IPerson 2 { 3 void eat(object food); //接口的方法 4 void speak(string text); //接口的方法 5 6 string name //接口的屬性 7 { 8 get; 9 set; 10 } 11 }
如前所述,接口中不允許定義變量、構造函數,不能顯示指定接口中方法或屬性的可訪問性。
接口可以被類或者別的接口繼承。類繼承接口的語法與類繼承類的語法相同,都是在類名後面加冒號和要繼承的接口名,如下:
訪問修飾符 class 類名:接口名
{
//類與接口的成員
}
類繼承接口與類繼承類的不同之處在於一個類可以同時繼承多個接口,但只能從一個類繼承。當一個類繼承多個接口時,多個接口之間用逗號隔開,如下:
訪問修飾符 class 類名:[基類名][,接口1][,接口2][...][,接口n]
{
//類與接口成員
}
接口繼承接口與類繼承接口語法類似,只是定義接口時使用關鍵字interface。
訪問修飾符 interface 接口名:[接口1][,接口2][,接口3][...][,接口n]
{
//接口成員
}
如果一個類從接口繼承,那麼這個類必須要實現接口中定義的所有方法和屬性。由於接口定義了方法和屬性的簽名,而這些方法和屬性的具體實現代碼是在從接口繼承的類裡寫,所以當一個類從接口繼承時,通常說一個類實現了某個接口。
這裡所說的實現有兩層含義:首先說明類繼承於接口,其次,類中用代碼實現了接口中定義的方法和屬性。
如果一個類實現了一個接口,由於類繼承自接口,所以類可以隱式轉換為接口,這與派生類向基類隱式轉換是一樣的。如果一個類實現了多個接口,那麼類可以隱式轉換為其中任意一個接口。
1 public interface IPerson 2 { 3 void eat(object food); //接口的方法 4 void speak(string text); //接口的方法 5 6 string name //接口的屬性 7 { 8 get; 9 set; 10 } 11 } 12 13 public class Student:IPerson 14 { 15 16 public void eat(object food) 17 { 18 Console.WriteLine(name + " eat: " + food.ToString()); 19 } 20 21 public void speak(string text) 22 { 23 Console.WriteLine(name + " say: " + text); 24 } 25 26 private string _name; 27 public string name 28 { 29 get 30 { 31 return _name; 32 } 33 set 34 { 35 _name = value; 36 } 37 } 38 } 39 static void Main(string[] args) 40 { 41 IPerson person; 42 Console.WriteLine("Main: 通過接口調用方法"); 43 person = new Student(); 44 person.name = "Nick"; 45 person.eat("apple"); 46 person.speak("Hello"); 47 Console.WriteLine("Main: 通過類調用方法"); 48 Student s = new Student(); 49 s.name = "Jessice"; 50 s.eat("rice"); 51 s.speak("Hehe"); 52 53 Console.ReadLine(); 54 }
結果
Main: 通過接口調用方法 Nick eat: apple Nick say: Hello Main: 通過類調用方法 Jessice eat: rice Jessice say: Hehe
顯式接口的實現
使用顯式接口時,類中用來實現接口的方法名前面必須以接口名作為前綴。
1 public class NewStudent:IPerson 2 { 3 4 void IPerson.eat(object food) 5 { 6 Console.WriteLine(_name + " eat: " + food.ToString()); 7 } 8 9 void IPerson.speak(string text) 10 { 11 Console.WriteLine(_name + " say: " + text); 12 } 13 14 private string _name; 15 string IPerson.name 16 { 17 get 18 { 19 return _name; 20 } 21 set 22 { 23 _name = value; 24 } 25 } 26 } 27 static void Main(string[] args) 28 { 29 IPerson person; 30 Console.WriteLine("Main: 通過接口調用方法"); 31 person = new Student(); 32 person.name = "Nick"; 33 person.eat("apple"); 34 person.speak("Hello"); 35 Console.WriteLine("Main: 通過類調用方法"); 36 Student s = new Student(); 37 s.name = "Jessice"; 38 s.eat("rice"); 39 s.speak("Hehe"); 40 41 Console.WriteLine("顯式接口示例"); 42 person = new NewStudent(); ; 43 person.name = "Jason"; 44 person.eat("Bread"); 45 person.speak("Good Luck!"); 46 47 NewStudent ns = new NewStudent(); 48 //ns.name = "Lucy"; //報錯,顯式接口只能通過接口來調用 49 50 Console.ReadLine(); 51 }
運行結果
Main: 通過接口調用方法 Nick eat: apple Nick say: Hello Main: 通過類調用方法 Jessice eat: rice Jessice say: Hehe 顯式接口示例 Jason eat: Bread Jason say: Good Luck!
在顯式實現接口時,NewStudent類中對應於IPerson接口的方法(或屬性)名都有IPerson做前綴,而且方法(或屬性)不允許有public、protected等訪問修飾符。當一個類顯式實現接口時,類中用於實現接口的方法只能通過接口
來調用,而不能通過接口的實例來調用。
接口與抽象類的對比
接口與抽象類有相似之處,兩者都可以定義一組屬性和方法,都不能被創建實例,只能用作別的類的基類。但接口與抽象類有很大不同之處,如下:
1、在抽象類中可以定義變量,而在接口中不可以
2、在抽象類中可以定義構造函數,而接口中不可以
3、在抽象類中可以定義非公共成員,如protected、private、internal等級的方法變量等,接口中只能定義public的成員
4、抽象類中的非抽象方法可以有方法體,而接口中的方法只能有定義,不能有實現
5、由於C#類只能單繼承,所以一旦一個類繼承了某個抽象類,那麼就無法繼承其他抽象類了。也就是說,抽象類的繼承具有唯一性和排他性。而從接口繼承卻不存在這個問題。一個類繼承某個接口,不影響這個類在繼承其他接口
綜上,在接口中只能定義public訪問級別的方法簽名和屬性簽名,而抽象類除了不能生成類的實例外,其余的行為與普通類相同,能在普通類中定義的所有成員,都可以在抽象類中定義。抽象類中的方法允許定義實現代碼,從而可以
把派生類中的公共代碼放在抽象類中,實現代碼復用,減少派生類的編碼量。
接口與抽象類例子如下:
1 interface IPerson 2 { 3 void eat(object food); //接口的方法 4 void speak(string text); //接口的方法 5 6 string name //接口的屬性 7 { 8 get; 9 set; 10 } 11 } 12 13 public abstract class Person 14 { 15 public void eat(object food) 16 { 17 Console.WriteLine(name + " eat: " + food.ToString()); 18 } 19 20 public void speak(string text) 21 { 22 Console.WriteLine(name + " say: " + text); 23 } 24 25 private string _name; 26 public string name 27 { 28 get { return _name; } 29 set { _name = value; } 30 } 31 } 32 33 public class Child1:IPerson 34 { 35 36 public void eat(object food) 37 { 38 Console.WriteLine(name + " eat: " + food.ToString()); 39 } 40 41 public void speak(string text) 42 { 43 Console.WriteLine(name + " say: " + text); 44 } 45 46 private string _name; 47 public string name 48 { 49 get 50 { 51 return _name; 52 } 53 set 54 { 55 _name = value; 56 } 57 } 58 59 public void Study() 60 { 61 Console.WriteLine(name + " study hard....."); 62 } 63 } 64 65 public class Adult1:IPerson 66 { 67 public void eat(object food) 68 { 69 Console.WriteLine(name + " eat: " + food.ToString()); 70 } 71 72 public void speak(string text) 73 { 74 Console.WriteLine(name + " say: " + text); 75 } 76 77 private string _name; 78 public string name 79 { 80 get 81 { 82 return _name; 83 } 84 set 85 { 86 _name = value; 87 } 88 } 89 90 public void Work() 91 { 92 Console.WriteLine(name + " work hard...."); 93 } 94 } 95 96 public class Child2:Person 97 { 98 public void Study() 99 { 100 Console.WriteLine(name + " study hard...."); 101 } 102 } 103 104 public class Adult2:Person 105 { 106 public void Work() 107 { 108 Console.WriteLine(name + " work hard..."); 109 } 110 } 111 112 static void Main(string[] args) 113 { 114 Child1 child1 = new Child1(); 115 Child2 child2 = new Child2(); 116 Adult1 adult1 = new Adult1(); 117 Adult2 adult2 = new Adult2(); 118 119 child1.name = "Jack"; 120 child1.eat("apple"); 121 child1.speak("hello"); 122 child1.Study(); 123 124 adult1.name = "Nick"; 125 adult1.eat("apple"); 126 adult1.speak("hello"); 127 adult1.Work(); 128 129 child2.name = "Lucy"; 130 child2.eat("rice"); 131 child2.speak("hello"); 132 child2.Study(); 133 134 adult2.name = "Lily"; 135 adult2.eat("banana"); 136 adult2.speak("hello"); 137 adult2.Work(); 138 }
運行結果
Jack eat: apple Jack say: hello Jack study hard..... Nick eat: apple Nick say: hello Nick work hard.... Lucy eat: rice Lucy say: hello Lucy study hard.... Lily eat: banana Lily say: hello Lily work hard...
從Child1和Adult1類的實現代碼來看,兩個類都繼承自IPerson,為了實現接口中定義的方法,相同的代碼在兩個類中寫了兩遍。
從Person、Child2和Adult2類代碼可以看出,在Person中實現了方法和屬性,不用再派生類中重復實現,實現了代碼的復用。