基础
定义接口的语法如下:
[attributes] [access-modifier] interface interface-name[:base-list]
{interface-body}
接口的目的是定义一些类所应具有的功能。
interface IStorable{
void Read( );
void Write( object obj );
int Status { get; set; }
}
interface ICompressible
{
void Compress( );
void Decompress( );
}
继承自接口的类必须实现接口声明的所有方法和属性。
接口中的方法不需要加 访问修饰符(access-modifier) ,否则会产生编译错误。接口中的方法隐式的被声明为public,因为接口是一个由其他类所使用的契约(contract)。
不能创建接口的实例,只能创建实现了接口的类的实例。实现了接口的类的实例,可以转换为这一接口类型。
WARNING:实现接口的类中的方法不可以是静态的(static)。
实现多个接口
public class Document : IStorable, ICompressible
扩展接口
interface ILoggedCompressible : ICompressible
{
void LogSavedBytes();
}
复合接口
interface IStorableCompressible : IStorable, ILoggedCompressible
{
void LogOriginalSize();
}
将类的实例转换为接口类型
Public class Document:IStorableCompressible
{
// implemention
}
Document doc = new Document();
IStorable isDoc = doc as IStorable;
IStorableCompressible iscDoc = doc as IStorableCompressible;
其中 IStorable isDoc = doc as IStorable也可直接写作 IStorable isDoc = doc
因为编译器知道Document类实现了IStorable接口,并会进行隐式转换。
如果不确定某个类是否实现了 IStrorable 接口,可以使用 as ,如果该类没有实现,将返回null。
IStrorable icDoc = someClassInstance as IStrorable;
if ( icDoc != null )
{
icDoc.Read( );
}
else
Console.WriteLine( "Read not supported" );
访问接口方法
以多态方式使用接口。
IStorable isDoc = new Document("Test Document");
icDoc.Read( );
将对象转换成接口类型
很多情况下,并不知道集合中的某个对象是否实现了接口。如果像下面这样使用接口:
Document doc = myCollection[0];
IStorable isDoc = (IStorable) doc;
isDoc.Read( );
ICompressible icDoc = (ICompressible) doc;
icDoc.Compress( );
而 Document的声明是这样的:
public class Document : IStorable
则将抛出异常。
使用 is 操作符
为了判断某个对象是否实现了特定接口,可以使用 is 操作符:
expression is type
如果 expression 可以转换成 type类型,则上面的表达式返回 true。
使用 is 的范例:
if (doc is IStorable)
{
IStorable isDoc = (IStorable)doc;
isDoc.Read( );
}
使用 as 操作符
也可以使用 as ,当某个对象不能转换为特定接口时,将返回 null。
expression as type
使用 as 的范例:
IStorable isDoc = doc as IStorable;
if (isDoc != null)
{
isDoc.Read( );
}
在类型转换时使用 as 优于使用 is(原因略)
抽象类 vs 接口
很多情况下 抽象类 和 接口可以互相转换,而且区别甚微。
abstract class Storable
{
abstract public void Read();
abstract public void Write();
}
使用接口的理由:
l 可以继承多个接口,但只能继承一个抽象类
使用抽象类的理由:
l 抽象类可以更好的进行版本控制。如果日后修改一个抽象类,只需再往里添加一个虚拟方法,并提供一个默认实现。而对于一个接口,如果添加一个新方法,则只有两种可能:与此接口相关的所有类都必须改写,以实现这个新添加的方法;可以新起一个接口,并让这个接口继承自老接口,如此一来,你将会获得非常多的接口,很难管理。
覆盖接口实现的方法
一个实现了接口的类,可以将实现接口的任何或者全部方法设为虚拟的。这样,继承此类的其他类可以对这些方法进行覆盖。
using System;
using System.Collections.Generic;
using System.Text;
namespace overridingInterface {
interface IStorable {
void Read();
void Write();
}
/* 实现IStorable 的简单Document
* ***************************************/
public class Document : IStorable {
public Document(string s) {
Console.WriteLine("Creating document with: {0}", s);
}
//虚拟方法
public virtual void Read() {
Console.WriteLine("Document Read Method for IStorable");
}
public void Write() {
Console.WriteLine("Document Write Method for IStorable");
}
}
/* 继承自Document 的类
* ***************************************/
public class Note : Document {
public Note(string s): base(s) {
Console.WriteLine("Creating note with: {0}", s);
}
// 覆盖Document中的虚拟方法
public override void Read() {
Console.WriteLine("Overriding the Read method for Note!");
}
// 实现一个Note的新Write方法
public new void Write() {
Console.WriteLine("Implementing the Write method for Note!");
}
}
public class Tester {
static void Main() {
Document theDoc = new Document("** Doc");
theDoc.Read();
theDoc.Write();
Console.WriteLine();
IStorable isDoc = theDoc as IStorable;
if (isDoc != null) {
isDoc.Read();
isDoc.Write();
}
Console.WriteLine("/n*************************************/n");
Document theNote = new Note("** Fist Note");
theNote.Read(); // 输出为覆盖了的Read() 方法
theNote.Write();
Console.WriteLine();
IStorable isNote = theNote as IStorable;
if (isNote != null) {
isNote.Read(); //输出为覆盖了的Read() 方法
isNote.Write();
}
Console.WriteLine("/n*************************************/n");
Note note2 = new Note("** Second Note");
note2.Read();
note2.Write();// 输出为使用new 的,重新实现了的Write()方法
Console.WriteLine();
IStorable isNote2 = note2 as IStorable;
if (isNote2 != null) {
isNote2.Read();
isNote2.Write();// 输出为Document 实现的的方法
}
Console.ReadKey();
}
}
}
输出为:
Creating document with: ** Doc
Document Read Method for IStorable
Document Write Method for IStorable
Document Read Method for IStorable
Document Write Method for IStorable
*************************************
Creating document with: ** Fist Note
Creating note with: ** Fist Note
Overriding the Read method for Note!
Document Write Method for IStorable
Overriding the Read method for Note!
Document Write Method for IStorable
*************************************
Creating document with: ** Second Note
Creating note with: ** Second Note
Overriding the Read method for Note!
Implementing the Write method for Note!
Overriding the Read method for Note!
Document Write Method for IStorable
显式实现接口方法
当一个类实现了两个接口(假设Document 类实现了IStorable和ITalk接口),但是两个接口中有方法名相同时,可以使用下面的语法来显式地实现一个接口:
void ITalk.Read()
显式实现接口的方法时,不可以加访问修饰符(access modifier),将隐式地声明为public。
不能通过类的实例来直接访问显式实现的方法。假设该类还实现了IStorable接口中的Read()的方法,当使用下面的语句时:
theDoc.Read( );
将会隐式调用IStorable的Read() 方法。
如果该类仅实现了ITalk接口,而没有实现IStorable接口,也就不存在方法名冲突的情况,但是却仍使用显示的接口声明,那么当使用 theDoc.Read() 时,将会出现编译错误。
'ExplicitImplementation.Document' does not contain a definition for 'Read' F:/MyApp/Test/ExplictImplament.cs 57 11 Test
当想使用 ITalk接口的方法时,需要进行一次类型转换,使用下面的语法:
ITalk itDoc = theDoc;
itDoc.Read();
成员隐藏
假设有如下两个接口:
interface IBase
{
int P { get; set; }
}
interface IDerived : IBase
{
new int P();
}
继承 IDerived的类至少需要进行一个显示实现。
class myClass : IDerived
{
int IBase.P { get {...} }
public int P( ) {...}
}
class myClass : IDerived
{
public int P { get {...} }
int IDerived.P( ) {...}
}
class myClass : IDerived
{
int IBase.P { get {...} }
int IDerived.P( ) {...}
}
实现接口的值类型(Struct)
如果使用值类型实现接口,则应通过值类型的对象访问接口方法,而不要转换成接口,再用接口进行访问,此时会多出一个“复制”了的引用对象,而原来的值对象依然存在,两个对象是各自独立的。
myStruct theStruct = new myStruct( );
theStruct.Status = 2;
IStorable isTemp = ( IStorable ) theStruct;
Console.WriteLine( "isTemp: {0}", isTemp.Status );
isTemp.Status = 4;
Console.WriteLine("theStruct:{0}, isTemp: {1}",theStruct.Status, isTemp.Status );
theStruct.Status = 6;
Console.WriteLine( "theStruct: {0}, isTemp: {1}",theStruct.Status, isTemp.Status );
输出为:
isTemp: 2
theStruct: 2, isTemp: 4
theStruct: 6, isTemp: 4