13.1 类和接口继承
接口是为了实现多继承
所有的类都继承了System.Object,及其四个公共实例方法:ToString、Equals、GetHashCode、GetType。
CLR允许定义接口,它实际只是对一组方法签名进行了统一命名。这些方法不提供任何实现。
类通过指定接口名称来继承接口,而且必须显式实现接口方法,否则CLR认为此类型定义无效。
实现接口方法的过程比较繁琐,所以说接口继承是实现多继承的一种缩水版机制。
编译器和CLR允许一个类继承多个接口,继承的所有接口都必须实现。
接口继承特点
类继承的一个重要特点是,凡是能使用基类型实例的地方,都能使用派生类型的实例。
类似的,凡是能够使用具名接口类型的实例的地方,都能使用实现了接口的一个类型的实例。
13.2 定义接口
接口还能定义事件、无参属性和有参属性。
所有这些东西的本质上都是方法,他们只是语法上的简化。
不过接口不能定义任何构造器方法,也不能定义任何实例字段。
接口不能定义静态成员
CLR允许接口定义静态方法、字段、常量和构造器,但符合CLS标准的接口决不允许,C#禁止接口定义任何一种静态成员。
接口定义就是类型定义
在CLR看来,接口定义就是类型定义。
也就是说CLR会为接口类型对象定义内部数据结构,同时可通过反射机制来查询接口类型的功能。
和类型一样,接口可在文件范围中定义,也可嵌套在另一个类型中。定义接口类型时可指定你希望的任何可见性、可访问性。
接口继承不是严格的继承
接口定义可以从另一个或多个接口继承。但它不是严格的继承。
接口继承的工作方式并不完全和类继承一样。
作者倾向于将接口结成看成是将其他接口的协定contract包括到2新接口中。
例如ICollection<T>接口定义包含了IEnumerable<T>和IEnumerable两个接口的协定,有下面两层含义:
1.继承ICollection<T>接口的任何类必须实现ICollection<T>、IEnumerable<T>和IEnumerable三个接口所定义的方法。
2.任何代码在引用实现ICollection<T>接口的对象时,该类型还实现了IEnumerable<T>和IEnumerable接口。
13.3 继承接口
显式为实现接口的方法标记virtual和隐式的区别
编译器要求将实现接口的方法标记为public。
CLR要求将接口方法标记为virtual,编译器会将他们标记为virtual和sealed。这会阻止派生类重写接口方法。
将方法显式标记为virtual,编译器就会将该方法标记为virtual并保持它的非密封状态,使派生类能重写它。
派生类重写基类实现的接口方法
派生类不能重写sealed的接口方法,但派生类可重新继承同一个接口,并为接口方法提供自己的实现。
下例对此进行了演示:
//派生自Object,他实现了IDisposable internal class Base:IDisposable{ public void Dispose(){ Console.WriteLine("Base's Dispose"); } } //重新实现了IDisposable internal class Derived:Base,IDisposable{ new public void Dispose(){ Console.WriteLine("Base's Dispose"); } }
在Main函数中进行调用:
/*****************第一个例子****************/ Base b=new Base(); //用b的类型调用Dispose,显示"Base's Dispose" b.Dispose(); //用b的对象的类型调用Dispose,显示"Base's Dispose" ((IDisposable)b).Dispose(); /*****************第二个例子****************/ Derived d=new Derived(); //用d的类型调用Dispose,显示"Derived's Dispose" d.Dispose(); //用d的对象的类型调用Dispose,显示"Derived's Dispose" ((IDisposable)d).Dispose(); /*****************第三个例子****************/ b=new Derived(); //用b的类型调用Dispose,显示"Base's Dispose" b.Dispose(); //用b的对象的类型调用Dispose,显示"Derived's Dispose" ((IDisposable)b).Dispose();