【温故知新】c#抽象类abstract与接口interface
1、什么是抽象类
先来看MSDN对抽象类描述:
抽象类是一些留有部分或全部成员未实现的类,以便可以由派生类来提供实现。
在面向对象的编程中,抽象类用作层次结构的基类,并表示不同对象类型组的通用功能。 正如名称“抽象”所暗指的,抽象类通常不会直接与问题域中的具体实体对应。 不过,抽象类会表示多个不同的具体实体之间的共同之处。
仅当存在已声明但未定义的抽象方法时,才会将类视为抽象类。 因此,具有抽象方法的类并不一定是抽象类。 除非类具有未定义的抽象方法,否则不要使用 AbstractClass 特性。
一个类只能从一个抽象(或任何其他类型)类继承。
那抽象又是什么呢?例如:汽车,卡车,电动车都可以抽象为车子,直升机,客机,战斗机,都可以抽象成飞机,而车子和飞机,都可以抽象成交通工具,逐步脱去“个性”,保留“共性”,这就是抽象,这也是面向对象多态继承的一部分。
当一个类抽象到其方法需要到子类中实现的时候,就应该使用抽象类而不是派生类。例如:直升机和客机,都能飞,但是在继承之前,你不知道飞机到底使用螺旋桨飞,还是用涡扇发动机飞,所以只能抽象成抽象类。
2、什么是接口
先来看MSDN的描述:
接口包含 类 或 结构 可以实现相关一组功能的定义。
由于C#语言不支持多重继承,但是可以实现多个借口,所以该功能很重要。
接口不能包含常数、字段、运算符、实例构造函数、析构函数或类型。 接口成员将自动是公共的,因此,它们不会包含任何访问修饰符。 成员也不能是 静态。
若要实现接口成员,实现的选件类的相应成员必须是公共的,非静态的,并且具有名称和签名和接口成员相同。
小时候玩过拼图,每个小图片通过卡扣一个个连接起来成为一幅图,这个小卡扣就是非常形象的“接口”,一个庞大的系统,也是通过每个模块的接口拼接起来的。
可以这么理解,一个接口就是一个类实现外界公布的方法约定,使我们知道这个类肯定有这么一个方法来实现我们的需求
例如:我们都知道汽车有“油门”,我们不用管这个车子是电动还是汽油还是柴油,不管用的什么高级还是低级发动机,只要轻轻踩一脚,车子就可以动起来,对外界来说,“油门”就相当于一个接口,汽车实现了这个接口,我们就知道可以通过踩油门来启动汽车。那如果哪天自行车也实现了“油门”,我们也不管那么多这自行车内部到底是怎么实现的(我不想知道也不用知道,对于消费者来说,和我无关),反正踩自行车的油门,自行车肯定就会自己动起来了。
所以接口是具有普遍“共性”的方法约定。
3、什么时候使用抽象类,接口?
直接看一段以“硬盘”为抽象的代码场景演示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace AbstractAndInterface
{
/// <summary>
/// 通用USB接口,插到电脑上,电脑只认这个方法,来读取插入东西的数据,对于其他东西都不管
/// </summary>
public interface USB_Interface
{
string USBReadData();
} /// <summary>
/// 通用蓝牙接口,开启蓝牙之后,电脑只认这个方法,来读取插入东西的数据,对于其他东西都不管
/// </summary>
public interface BlueTooth_Interface
{
string BlueToothReadData();
} /// <summary>
/// 硬盘抽象类
/// </summary>
public abstract class Disk
{
//抽象方法读数据,因为子类读取方法不能确定(HDD磁盘寻道,SSD闪存颗粒直接读),所以声明抽象函数,那么这个类肯定就要声明为抽象类。
public abstract string ReadData();
} /// <summary>
/// 实现了通过USB和蓝牙传输数据的接口
/// </summary>
public class SSD_Disk : Disk, USB_Interface, BlueTooth_Interface
{
public override string ReadData()
{
return "SSD通过闪存颗粒超高速读数据!";
} public string USBReadData()
{
return "通过USB数据传输:" + ReadData();
} public string BlueToothReadData()
{
return "通过BlueTooth数据传输:" + ReadData();
}
} /// <summary>
/// 实现了通过USB和蓝牙传输数据的接口
/// </summary>
public class HDD_Disk : Disk, USB_Interface, BlueTooth_Interface
{
public override string ReadData()
{
return "HDD通过磁性碟片读数据!";
}
public string USBReadData()
{
return "通过USB数据传输:" + ReadData();
} public string BlueToothReadData()
{
return "通过BlueTooth数据传输:" + ReadData();
}
} public class Computer
{
/// <summary>
/// 电脑只管通过接口来读取数据,不管你是什么设备,反正返回给我数据就行。
/// </summary>
/// <param name="usbDevice"> 实现usb接口的设备</param>
/// <returns></returns>
public string ReadUSB(USB_Interface usbDevice)
{
return usbDevice.USBReadData();
}
} class Program
{
static void Main(string[] args)
{
USB_Interface ssd = new SSD_Disk();
USB_Interface hdd = new HDD_Disk();
Computer computer = new Computer();
Console.WriteLine(computer.ReadUSB(ssd));
Console.WriteLine(computer.ReadUSB(hdd));
}
} }
对以上代码,我的总结如下:
1、Computer类通过USB_Interface来读取设备数据,这样这个USB设备对于Computer类来说是透明的(Disk没有曝光),Computer也不需要知道具体的类,只知道调用这个接口,你返回给我数据就行,提高内聚,低耦合,否则,如果有新的不是继承自Disk类的存储设备,Computer类岂不是要新写一个方法读取数据?
2、通过接口,我们能够很方便的对类进行功能扩展以及功能组合。否则使用抽象类,多一个功能就要继承一次,代码工作量非常大,子类划分也非常不明显
例如:
/// <summary>
/// 硬盘抽象类
/// </summary>
public abstract class Disk
{
//抽象方法读数据,因为子类读取方法不能确定(HDD磁盘寻道,SSD闪存颗粒直接读),所以声明抽象函数,那么这个类肯定就要声明为抽象类。
public abstract string ReadData();
} public abstract class UsbDisk : Disk
{
public abstract string USBReadData();
} public abstract class BlueToothDisk : Disk
{
public abstract string BlueToothReadData();
} public abstract class BlueToothAndUsbDisk : Disk
{
public abstract string USBReadData();
public abstract string BlueToothReadData();
}
3个不同功能的Disk产品的继承(类只能单继承),这还没有具体实现就已经感觉很复杂了,那如果要分别实现SSD和HDD,就更加复杂了。
在设计之初就应该发现,USB与BlueTooth其实和Disk没有太大的关联(是普遍产品具有的“共性”方法约定),它是一种外部数据通讯方式,应该提取为接口。但如果是Disk内部某两个部件之间的数据流通方法(子类实现方式不同),则可以定义为抽象函数。
3、面向接口编程,如果哪天蹦出一个新的存储设备,电脑不用做任何改变(新设备原理什么的我都不管,反正我调用接口,你就要返回给我数据),也可以直接读取新设备的数据(实际上USB通用接口的原理也是这样)。
最后看看MSDN上的总结:
- 如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。另一方面,接口一旦创建就不能更改。如果需要接口的新版本,必须创建一个全新的接口。
- 如果创建的功能将在大范围的全异对象间使用,则使用接口。抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。
- 如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。
- 如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。抽象类允许部分实现类,而接口不包含任何成员的实现。
【温故知新】c#抽象类abstract与接口interface的更多相关文章
- 抽象类 abstract 和 接口 interface 类的区别
在看一些框架的优秀改良时,都会设计一层base层,并且 base里面一般都是 abstract 类,然后 就找了为什么做的原因.发现: PHP5支持抽象类和抽象方法.抽象类不能直接被实例化,你必须先继 ...
- c#中抽象类(abstract)和接口(interface)的相同点与区别
相同点: 1.都可以被继承 2.都不能被实例化 3.都可以包含方法声明 4.派生类必须实现未实现的方法 区别: 1.抽象基类可以定义字段.属性.方法实现.接口只能定义属性.索引器.事件.和方法声明,不 ...
- Java:抽象类abstract和接口Interface
一.抽象类:abstract 抽象类就是为了继承而存在的,如果你定义了一个抽象类,却不去继承它,那么等于白白创建了这个抽象类,因为你不能用它来做任何事情.对于一个父类,如果它的某个方法在父类中实现出来 ...
- C#中抽象类(abstract)和接口(interface)的实现
抽象类 抽象方法是没有代码实现的方法,使用abstract关键字修饰: 抽象类是包含0到多个抽象方法的类,其不能实例化.含有抽象方法的类必须是抽象类,抽象类中也可以包含非抽象方法: 重写抽象类的方法用 ...
- C++虚函数virtual,纯虚函数pure virtual和Java抽象函数abstract,接口interface与抽象类abstract class的比较
由于C++和Java都是面向对象的编程语言,它们的多态性就分别靠虚函数和抽象函数来实现. C++的虚函数可以在子类中重写,调用是根据实际的对象来判别的,而不是通过指针类型(普通函数的调用是根据当前指针 ...
- C# 抽象方法及抽象类 Abstract 及接口
public abstract class Animal { public abstract void Dosth(); } 该类中只有虚方法 那么我们可以用abstract来修饰 将该类称为抽象 ...
- python抽象类+抽象方法实现接口(interface)
#python没有类似于java和C#的接口类(interface),需要使用抽象类 和抽象方法来实现接口功能 #!/usr/bin/env python#_*_ coding:utf-8 _*_ f ...
- 虚方法virtual、抽象方法abstract、接口interface区别
接口.抽象类.抽象方法.虚方法: 这四个名词时非常容易混淆的: 首先说一下接口 与抽象类 的异同: 相同点: 1.他们都不能实例化自己,也就是说都是用来被继承的. 2.抽象类中的抽象方法和接口方法一样 ...
- 抽象 abstract 和 接口 interface。 java 的 堆 和 栈。 参数传递(基本类型和普通对象的区别)
package com.test; import com.test.Pro; //protected 修饰的成员只能在本包中和 继承该类的方法中使用 public abstract class Tes ...
随机推荐
- ArcGIS坐标转换
我忘了怎么设置坐标系了- 定义投影ArcCatalog设置? -arctoolbox好像都可以 感觉不用想的那么复杂]直接定义投影就行了 选这一个吗 这个就行了' 然后? 应该是先定义成 ...
- 明码——第九届蓝桥杯C语言B组(省赛)第二题
原创 标题:明码 汉字的字形存在于字库中,即便在今天,16点阵的字库也仍然使用广泛.16点阵的字库把每个汉字看成是16x16个像素信息.并把这些信息记录在字节中. 一个字节可以存储8位信息,用32个字 ...
- Spring jdbcTemplate RowMapper绑定任意对象
RowMapper可以将数据中的每一行封装成用户定义的类,在数据库查询中,如果返回的类型是用户自定义的类型则需要包装,如果是Java自定义的类型,如:String则不需要,Spring最新的类Simp ...
- TSQL--删除正在运行的数据库
); SET @dbName='DB1_SNAP' BEGIN TRY --===================================== --查找当前数据库所有连接并删除 DECLARE ...
- Redis缓存穿透、缓存雪崩
缓存穿透 缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到存储层去查询,失去了缓存的意义. ...
- 871. Minimum Number of Refueling Stops
A car travels from a starting position to a destination which is target miles east of the starting p ...
- CSS3 transition 属性——逐渐变慢/匀速/加速/减速/加速然后减速
ease: 1.ease:(逐渐变慢)默认值 2.linear:(匀速) 3.ease-in:(加速) 4.ease-out:(减速) 5.ease-in-out:(加速然后减速) 6.cubic-b ...
- 【转】OracleOraDb11g_home1TNSListener服务启动后又停止了
源地址:https://www.cnblogs.com/Asa-Zhu/p/3819605.html 一.错误描述 登陆PL/SQL Developer登陆本地数据库时先报没有监听程序,查看服务发现O ...
- P3705 [SDOI2017]新生舞会 01分数规划+费用流
$ \color{#0066ff}{ 题目描述 }$ 学校组织了一次新生舞会,Cathy作为经验丰富的老学姐,负责为同学们安排舞伴. 有\(n\)个男生和\(n\)个女生参加舞会买一个男生和一个女生一 ...
- docker构建mysql容器及Navicat 远程连接
1. MySQL部署 1.1拉取MySQL镜像 docker pull mysql 查看镜像 docker images 1.2创建MySQL容器 首先建立所需要的 文件夹: docker run - ...