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中实现了方法和属性,不用再派生类中重复实现,实现了代码的复用。