C#本质论之接口

先描述接口的概念

C#并非只能通过继承使用多态性,也可以通过接口使用。和抽象类不同,接口不包含任何实现(C++并不会特意去区分抽象类和接口,实际上C++的接口就是通过抽象类实现,其并没有interface和abstract关键字)。

接口的意义在于能够将实现细节和服务完全隔离开。举个例子:我们都知道插座可以供电,只要我们的插头能够插入插座就可以获得电力,也就是说,我们并不关心插座当中的电力是来自于核电、煤电还是太阳能发电,我们只关心我们的插头是否能插入插座进而充电。在这里插座就是接口,插座当中的电力的形式就是实现细节(核电、煤电、太阳能发电等),而插座的供电能力就是服务。因此这里的“将实现细节和服务完全隔离开”指的就是:无论插座(接口)当中的电力来源(实现细节)是什么形式,都不会影响插座(接口)的供电能力(服务)。这就是所谓“将实现细节和服务完全隔离开”。

那么在编程的时候,假如涉及到多态性,如何在继承和接口之间取舍呢?这里有个核心思想可以决定:继承既可以共享成员签名,又可以共享实现,当满足这两个条件的时候应当使用继承;接口只能共享成员签名,而不共享实现,当只满足这种情况的时候应当考虑使用接口。举个例子:都知道压缩有各种各样的格式:.zip、.cab、.tar、.tar.gz、.rar、.gz等。而每个压缩格式的实现方式都不同,然而它们都属于压缩的范畴,假如为每个压缩格式单独创建一个类,又无法提供标准的调用规范。这里就要考虑是使用继承还是接口。先假设使用继承,因为各种压缩格式实现方式不同,因此基类当中并不存在通用的代码供各种压缩格式派生类调用,也就是并没有共享实现,这会使得基类毫无意义(继承核心思想:共享成员标签(满足),共享实现(不满足))。因此应当使用接口(接口核心思想:共享成员标签(满足),不共享实现(满足))。压缩接口代码如下:

interface IFileCompression
{
	void Compress(string targetFileName,string[] fileList);
	void Uncompress(string compressedFileName, string expandDirectoryName);
}

只要各种压缩格式实现了这个接口以及接口中的方法,那么在需要压缩的时候,只需执行到IFileCompressoin接口的一次转型,然后调用它的成员方法即可,根本不用关心具体是什么类在实现哪些方法(接口是一致的),这正是多态性的一个反映。(后面以较为完整的代码诠释接口实现多态性)

要注意几点:1、接口既不包含实现,也不包含数据。2、C#不允许为借口成员使用访问修饰符,所有成员自动定义为public成员。

再描述接口实现多态性

//接口
interface IListable
{
	//接口可以提供属性,但是不能实现属性
	string[] ColumnValues
	{
		get;
	}
}
//抽象类
public abstract class PdaItem
{
	public PdaItem(string name)
	{
		Name = name;
	}
	public virtual string Name{get;set;}
}

//C#只能是单继承,实现接口的类另当别论
class Contact: PdaItem,IListable
{
	public Contact(string firstName, string lastName,
		string address, string phone):base(null)
	{
		FirstName = firstName;
		LastName = lastName;
		Address = address;
		Phone = phone;
	}
	public string FirstName {get;set;}
	public string LastName {get;set;}
	public string Address {get;set;}
	public string Phone {get;set;}
	//实现了接口的方法
	public string[] ColumnValues
	{
		get
		{
			return new string[]
			{
				FirstName,
				LastName,
				Phone,
				Address
			}
		}
	}
	
	public static string[] Headers
	{
		get
		{
			return new string[]
			{
				"First Name","Last Name		",
				"Phone		",
				"Address"
			}
		}
	}
}

class Publication : IListable
{
	public Publication(string title,string author,int year)
	{
		Title = title;
		Author = author;
		Year = year;
	}
	public string Title {get;set;}
	public string Author{get;set;}
	public int Year {get;set}
	
	//实现接口的方法
	public string[] ColumnValues
	{
		get
		{
			return new string[]
			{
				Title,
				Author,
				Year.ToString()
			}
		}
	}
	
	public static string[] Headers
	{
		get
		{
			return new string[]
			{
				"Title			",
				"Author		",
				"Year"
			}
		}
	}
}

class Program
{
	
	//类Contact和Publication毫不相关,然而由于其实现了接口的方法的缘故,在传递参数赋值给IListable的时候,调用的方法的实现细节是相应具体类型的方法实现
	public static void Main()
	{
		Contact[] contacts = new Contact[3];
		contacts[0] = new Contact(
			"Dick", "Traci",
			"123 Main St., Spokane, WA 99037",
			"123-123-1234"
		);
		contacts[1] = new Contact(
			"Andrew","Littman",
			"1417 Palmary St., Dallas, TX 55555",
			"555-123-4567"
		);
		contacts[2] = new Contact(
			"Mary", "Hartfelt",
			"1520 Thunder Way, Elizabethton, PA 44444",
			"444-123-4567"
			);
		
		ConsoleListControl.List(Contact.Headers,contacts);
		Console.WriteLine();
		
		Publication[] publications = new Publication[3]{
			new Publication(
				"Celebration of Discipline",
				"Richard Foster", 1987
			),
			new Publication(
				"Orthodoxy",
				"G.K. Chesteron",1908
			),
			new Publication(
				"The Hitchhiker's Guide to the Galaxy",
				"Douglas Adams",1979
			)
		};
		ConsoleListControl.List(Publication.Headers,publications);
	}
}

class ConsoleListControl
{
	public static void List(string[] headers,IListable[] items)
	{
		int[] columnWidths = DisplayHeaders(headers);
		for(int count = 0; count < items.Length; count++)
		{
			string[] values = items[count].ColumnValues;
			DisplayItemRow(columnWidths,values);
		}
	}
	
	private static int[] DisplayHeaders(string[] headers)
	{
	}
	private static vod DisplayItemRow(int[] columnWidths,string[] values)
	{
	}
}

要实现的接口和基类名称以逗号分隔(基类在前,接口顺序任意),类可以实现多个接口但是只能从一个基类直接派生。接口不能实例化,也不能使用new创建接口,因此接口没有构造器和终结器。

C#本质论(第3版) 详细介绍C# 4.0 第1章 c#概述 1.1 hello world 1.2 c#语法基础 1.2.1 c#关键字 1.2.2 类型定义 1.2.3 main 1.2.4 语句和语句分隔符 1.2.5 空白 1.3 使用变量 1.3.1 数据类型 1.3.2 变量的声明 1.3.3 变量的赋值 1.3.4 变量的使用 1.4 控制台输入和输出 1.4.1 从控制台获取输入 1.4.2 将输出写入控制台 1.5 注释 1.6 托管执行和公共语言基础结构 1.7 c#和net版本 .1.8 cil和ildasm 1.9 小结 第2章 数据类型 2.1 基本数值类型 2.1.1 整数类型 2.1.2 浮点类型 2.1.3 decimal类型 2.1.4 字面值 2.2 更多基本类型 2.2.1 布尔类型 2.2.2 字符类型 2.2.3 字符串 2.3 null和void 2.3.1 null 2.3.2 void 2.4 类型的分类 2.4.1 值类型 2.4.2 引用类型 2.5 可空修饰符 2.6 数据类型之间的转换 2.6.1 显式转型 2.6.2 隐式转型 2.6.3 不进行转型的类型转换 2.7 数组 2.7.1 数组的声明 2.7.2 数组的实例化和赋值 2.7.3 数组的使用 2.7.4 字符串作为数组使用 2.7.5 常见错误 2.8 小结 第3章 运算符和控制流 3.1 运算符 3.1.1 一元运算符正和负 3.1.2 二元算术运算符 3.1.3 圆括号运算符 3.1.4 赋值运算符 3.1.5 递增和递减运算符 3.1.6 常量表达式 3.2 流控制概述 3.2.1 if语句 3.2.2 嵌套if 3.3 代码块 3.4 作用域和声明空间 3.5 布尔表达式 3.5.1 关系运算符和相等性运算符 3.5.2 逻辑布尔运算符 3.5.3 逻辑求反运算符 3.5.4 条件运算符 3.5.5 空接合运算符 3.6 按位运算符 3.6.1 移位运算符 3.6.2 按位运算符 3.6.3 按位赋值运算符 3.6.4 按位取反运算符 3.7 控制流语句 3.7.1 whi.1 e和do/while循环 3.7.2 for循环 3.7.3 foreach循环 3.7.4 switch语句 3.8 跳转语句 3.8.1 break语句 3.8.2 continue语句 3.8.3 go to语句 3.9 c#预处理器指令 3.9.1 排除和包含代码 3.9.2 定义预处理器符号 3.9.3 生成错误和警告 3.9.4 关闭警告消息 3.9.5 nowarn:选项 3.9.6 指定行号 3.9.7 可视编辑器提示 3.10 小结 第4章 方法和参数 4.1 方法的调用 4.1.1 命名空间 4.1.2 类型名称 4.1.3 作用域 4.1.4 方法名称 4.1.5 参数 4.1.6 方法返回值 4.1.7 语句与方法调用的比较 4.2 方法的声明 4.2.1 参数声明 4.2.2 方法返回值声明 4.3 uslng指令 4.4 main()的返回值和参数 4.5 参数 4.5.1 值参数 4.5.2 引用参数 4.5.3 输出参数 4.5.4 参数数组 4.6 递归 4.7 方法重载 4.8 可选参数 4.9 用异常实现基本错误处理 4.9.1 捕捉错误 4.9.2 使用throw语句报告错误 4.10 小结 第5章 类 5.1 类的定义和实例化 5.2 实例字段 5.2.1 实例字段的声明 5.2.2 实例字段的访问 5.3 实例方法 5.4 使用this关键字 5.5 访问修饰符 5.6 属性 5.6.1 属性的声明 5.6.2 自动实现的属性 5.6.3 命名规范 5.6.4 提供属性验证 5.6.5 读和只写属性 5.6.6 为取值方法和赋值方法指定访问修饰符 5.6.7 属性作为虚字段使用 5.6.8 属性和方法调用不允许作为ref或out参数值使用 5.7 构造器 5.7.1 构造器的声明 5.7.2 默认构造器 5.7.3 对象初始化器 5.7.4 构造器的重载 5.7.5 使用this调用另一个构造器 5.8 静态成员 5.8.1 静态字段 5.8.2 静态方法 5.8.3 静态构造器 5.8.4 静态属性 5.8.5 静态类 5.9 扩展方法 5.10 封装数据 5.10.1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值