Csharp读书笔记(五)-接口与抽象类
接口
基本概念
- 接口告诉类必须实现某些方法和属性
- 如果没有实现,编译器会报错
- 接口可以定义一个类中必须有的方法,这样一个类只要实现接口,就可以做特定的事情
- 使用
interface
关键字定义接口 - 接口不能添加任何字段,因为它不存储数据(但是可以包含属性)
- 命名规范:接口名称以
I
开头 - 只需要在接口中添加方法名和参数,因为接口不做任何事情
- 接口中的所有方法都是抽象方法,不能有方法体 ^bbfa07
- 一个类只能继承一个类,但可以实现多个接口 ^018e94
- 不可以实例化接口,不过可以引用接口
- 接口就像清单,指出一个类可以做些什么事情
为什么使用接口?
- 接口并不是为了避免重复代码,继承才是
- 如果有一件事,多个类都能实现,你需要这样一个类作为参数,但不希望继承时(比如你觉得根本用不到那些东西,不想让子类继承基类的方法和属性),这时你无法用继承中的基类来作为参数,那么使用接口,就可以知道:只要类实现了这个接口,它就能做这件事,可以作为参数使用,而不必知道它到底是什么类型
- 如果有多件事,一个类能实现它们,这时无法使用继承(只能继承一个类),就需要使用接口
- 继承只是给类“分层”(减少重复),接口则可以给类“分类”(可以规定这个类可以干什么)
- 也就是说,当继承给你的类增加太多“负担”时,就应该考虑使用接口
- 举个例子,假设你有一个电器类,很多类都继承了它,比如电脑、电视、冰箱……但是现在你想要用一个可以处理食物的类作为参数(比如面包机、烤箱、微波炉……),这时无法使用继承(因为你只能继承一个类),就需要编写一个可处理食物的接口,把要用到的方法属性放进去,在可处理食物的类中实现这个接口,把参数改成这样一个接口引用,就可以解决这个问题
- C#拒绝继承多个类,并通过接口提供保护,这是为了避免二义性
- 二义性:当两个类分别继承一个基类,并且重写了基类中的同一个方法,这时如果允许继承多个类(这称为多重继承),那么继承这两个类的子类调用该方法(假设子类没有重写)时应该调用哪个方法?这就会出现问题:子类不知道应该调用哪个方法,因为继承了两个同名的不同方法
- 这也被称作“致命的死亡菱形”(如果[[Csharp/Csharp读书笔记/Csharp读书笔记(一)-对象与引用#创建类图|画出类图]],会发现这像一个菱形)
- 所以C#通过接口提供保护,接口并没有方法体(它不做任何事情),因为它没有方法体,所以一个类可以实现多个接口,即使接口中有同名方法,这个类也只能实现这一个方法,避免了二义性
接口引用
- 接口引用与对象引用类似,可以保持一个类不被垃圾回收
- 可以移动接口引用,就像移动标签一样
- 一个类需要实现这个接口,才能添加该接口引用
- 使用接口引用访问类时,只能访问该接口引用所对应的方法和属性 ^46c854
类与接口的强制转换
- 可以使用
is
关键字来查看一个类是否实现了这个接口 - 可以用
as
关键字来把一个类看成实现了这个接口(前提是使用is
找出这个类确实实现了) - 为什么使用as:有时候,C#并不知道一个类实现了一个接口(使用了其他接口引用),所以使用is之后还要让C#认为它确实实现了
- 举例: ^537c57
1 | //这里仅给出核心代码 |
- 在上面的例子中,C#知道computers数组中的类都实现了IComputer(因为这是个IComputer接口的数组),所以只能调用这些类中IComputer接口中包含的方法和属性(可以把接口看作一个清单,这里只能调用清单中的内容),而当使用
is
关键字找出这个类确实实现了IServer接口时,C#仍然不认为它实现了IServer(因为你根本没有告诉它这么认为,只是让他检查了这个“清单”是否与类匹配,这是is
关键字所做的),所以你需要使用as
关键字,这样C#就会认为它确实实现了IServer,就可以(且只能)调用IServer中的方法、属性了 - 由子类代替基类时,这称为向上强制转换
- 由基类代替子类时,这叫做向下强制转换
- 接口也可以这么做(上面的举例就是一个接口向下强制转换的例子)
接口的继承
- 接口可以继承其他接口
- 实现继承其他接口的接口时,必须实现所有继承的接口
访问修饰符
- public表示任何其他类都可以访问
- private表示只有该类中的成员及该类的其他实例可以访问
- protected表示对于该类的其他成员来说相当于private,而对于子类来说是public,也就是说子类成员可以访问子类成员及任何基类成员(注意:基类声明为protected后,无需使用base也可以访问基类中的公共类型)
- internal表示只能从程序集或子类访问
- sealed表示不能继承该类
抽象类
- 抽象类不能实例化
- 有时候,你想在一个类中完成一部分代码,在子类中再完成其余部分,于是基类根本不应该被实例化(它并不能工作——因为你没有完成它),这时应该使用抽象类,防止你不小心创建了本不应该创建的实例
- 比如说,一个方法需要一个字段来计算,但是在基类中不想设置它的值,想在子类中设置,这时基类是不完整的,它无法计算,所以设置为抽象类,防止误实例化
- 可以在抽象类中添加抽象方法,也可以有具体方法,这与接口不同
面向对象的设计原则
本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 SamHou's blog!
评论