在C#中不可以继承多个基类,但是可以实现多个接口。但是当实现的不同接口中拥有相同名称的方法时,我们就必须使用限定的完全名称来在类中实现接口的方法,比如:
... {
string GetConnStr() ...{}
}
public interface IOracle
... {
string GetConnStr() ...{}
}
public class DB: IMySQL, IOracle
... {
string IMySQL.GetConnStr() ...{}
string IOracle.GetConnStr() ...{}
public string GetConnStr() ...{}
}
在上面的代码中,两个接口IMySQL、IOracle有一个相同名称的方法GetConnStr,当类DB继承这两个接口时,必须使用完全限定名称来实现,也就是“接口名.方法名”。代码中的IMySQL.GetConnStr()和IOracle.GetConnStr()分别是接口IMySQL和IOracle中GetConnStr方法的实现(显式接口继承),而第三个GetConnStr方法则与两个接口没有任何关系(如果删除IMySQL.GetConnStr方法,第三个GetConnStr方法则成为IMySQL接口的实现方法)。
注意在两个显式接口成员实现的方法前面并没有public或private之类的访问限定符,这是因为有时候它们是public,有时候又是private,为什么这样说呢,请先看下例:
... {
DB db = new DB();
string connectionString;
connectionString = db.GetConnStr();
connectionString = ((IMySQL)db).GetConnStr();
connectionString = ((IOracle)db).GetConnStr();
}
在此的第一个GetConnStr方法调用(connectionString = db.GetConnStr())实际上是调用了开始代码段中的第三个GetConnStr方法,即与任何接口无关的那个方法。在这个调用发生时,两个接口的GetConnStr方法是私有的(private),继承接口的类的实例并不能调用接口的方法。而当类的实例被转化成接口的类型时,GetConnStr方法成为了公开方法(public)。
接口的灵活性也会伴随着编译时类型安全的代价,因为很多的接口都接受System.Object类型的参数或者返回一个System.Object类型的值,比如常用的IComparable接口:
... {
// Methods
int CompareTo(object obj);
}
当我们定义一个值类型实现IComparable接口的CompareTo方法时:
... {
private Int32 x;
public ValueType(Int32 x) ...{ this.x = x; }
public Int32 CompareTo(Object obj)
...{
return (x - ((ValueType)obj).x);
}
}
然后调用这个类型的CompareTo方法:
... {
ValueType v = new ValueType(0);
Object o = new Object();
Int32 n = v.CompareTo(o);
}
上面这段代码中声明了一个Object类型的对象o,然后调用ValueType的CompareTo方法来接受o进行比较,这在编译期间没有任何错误发生,但是当运行时就会出现类型转换错误,这是因为o并不是ValueType类型,并没有一个x的字段。
CLR提供的显式接口成员实现可以帮助我们在编译时获得错误提示,从而避免程序运行后发生错误,提高类型安全,请看下面代码:
... {
private Int32 x;
public ValueType(Int32 x) ...{ this.x = x; }
public Int32 CompareTo(ValueType vt)
...{
return (x - vt.x);
}
Int32 IComparable.CompareTo(Object obj)
...{
return CompareTo((ValueType)obj);
}
}
在这段代码中提供了两个版本的CompareTo方法,第二个就是我们前面提到过的显式接口成员实现,让我们来看这样做的好处:
ValueType vt2 = new ValueType( 2 );
Int32 n;
n = vt1.CompareTo(vt2);
n = vt2.CompareTo( new Object()); // compile-time error
// 调用接受Object类型参数的IComparable.CompareTo方法,vt2被进行装箱操作
n = ((IComparable)vt1).CompareTo(vt2);
在第一次调用(n = vt1.CompareTo(vt2))中,使用的是接受ValueType类型参数的CompareTo方法,这样CLR就会在编译时进行类型检查,保证了类型安全,而之后的n = vt2.CompareTo(new Object());则会碰到编译时错误。如果我们想调用接受Object类型参数的CompareTo方法,就必须获得IComparable类型的一个引用,所以在最后一句中我们进行了类型转换并调用CompareTo方法。但是在最后一次的调用中vt1首先被装箱转换成IComparable类型,接下来因为IComparable.CompareTo方法接受Object类型参数,所以vt2被装箱转换成Object类型。
综上所述,显式接口成员实现帮助我们增强类型安全(无论是对值类型还是引用类型),对于值类型,还可以避免装箱操作,减少性能损耗。