C#面向对象 之 多态

R | | 访问(25)

  案例代码:

// 可支持文件类型,以文件扩展名划分 
enum FileType
{
	doc,// Word文档 
	pdf,// PDF文档  
	txt,// 文本文档
	ppt,// Powerpoint文档     
	jpg,// jpg格式图片     
	gif,// gif格式图片    
	mp3,// mp3音频文件    
	avi // avi视频文件 
}

interface IFileOpen
{
	void Open();
}

abstract class Files : IFileOpen
{
	private FileType fileType = FileType.doc;
	public FileType FileType
	{
		get { return fileType; }
	}
	public abstract void Open();

	public void Delete()
	{
		//实现对文件的删除处理    
	}
}

abstract class DocFile : Files
	{
		public int GetPageCount()
		{
			// 计算文档页数
			return 0;
		}
	}

abstract class ImageFile : Files
{
	public void ZoomIn()
	{
		// 放大比例 
	}

	public void ZoomOut()
	{
		// 缩小比例      
	}
}

abstract class MediaFile : Files
{

}

class WORDFile : DocFile
{
	public override void Open()
	{
		Console.WriteLine("Open the WORD file.");
	}
}

class PDFFile : DocFile
{
	public override void Open()
	{
		Console.WriteLine("Open the PDF file.");
	}
}

class JPGFile : ImageFile
{
	public override void Open()
	{
		Console.WriteLine("Open the JPG file.");
	}
}

class AVIFile : MediaFile
{
	public override void Open()
	{
		Console.WriteLine("Open the AVI file.");
	}
}

class MPEGFile : MediaFile
{
	public override void Open()
	{
		Console.WriteLine("Open the MPEG file.");
	}
}

class LoadManager
{
	private IList<Files> files = new List<Files>();

	public IList<Files> Files
	{
		get { return files; }
	}
	public void LoadFiles(Files file)
	{
		files.Add(file);
	}

	// 打开所有资料   
	public void OpenAllFiles()
	{
		foreach (IFileOpen file in files)
		{
			file.Open();
		}
	}

	// 打开单个资料   
	public void OpenFile(IFileOpen file)
	{
		file.Open();
	}

	// 获取文件类型   
	public FileType GetFileType(string fileName)
	{
		// 根据指定路径文件返回文件类型     
		FileInfo fi = new FileInfo(fileName);
		return (FileType)Enum.Parse(typeof(FileType), fi.Extension);
	}
}

  1、多态的分类

  多态有多种分类的方式,Luca Cardelli在《On Understand-ing Types, Data Abstraction, and Polymorphism》中将多态分为四类:强制的、重载的、参数的和包含的。上面的案例代码可以理解为包含的多态,从面向对象的角度来看,根据其实现的方式我们可以进一步分为基类继承式多态和接口实现式多态。

  (1)基类继承式多态基类继承多态的关键是继承体系的设计与实现,在FileLoader系统中File类作为所有资料类型的基类,然后根据需求进行逐层设计,我们从架构设计图中可以清楚地了解继承体系关系。在客户端调用时,多态是以这种方式体现的:

  Files myFile = new WORDFile();

  myFile.Open();

  myFile是一个父类Files变量,保持了指向子类WORDFile实例的引用,然后调用一个虚方法Open,而具体的调用则决定于运行时而非编译时。从设计模式角度看,基类继承式多态体现了一种IS-A方式,例如WORDFile IS-A Files就体现在这种继承关系中。

  (2)接口实现式多态多态并非仅仅体现在基于基类继承的机制中,接口的应用同样能体现多态的特性。区别于基类的继承方式,这种多态通过实现接口的方法约定形成继承体系,具有更高的灵活性。从设计模式的角度来看,接口实现式多态体现了一种CAN-DO关系。同样,在万能加载器的客户端调用时,也可以是这样的实现方式:

  IFileOpen myFile = new WORDFile();

  myFile.Open();

  当然,很多时候这两种方式都是混合应用的,就像FileLoader系统的实现方式。

  2、多态的运行机制

  从技术实现角度来看,是.NET的动态绑定机制成就了面向对象的多态特性。那么什么是动态绑定,.NET又是如何实现动态绑定呢?

  动态绑定,又叫晚期绑定,是区别与静态绑定而言的。静态绑定在编译期就可以确定关联,一般是以方法重载来实现的;而动态绑定则在运行期通过检查虚拟方法表来确定动态关联覆写的方法,一般以继承和虚方法来实现。在.NET中,虚方法以virtual关键字来标记,在子类中覆写的虚方法则以override关键字标记。从设计角度考量,通常将子类中共有的但却容易变化的特征抽取为虚函数在父类中定义,而在子类中通过覆写来重新实现其操作。

  注意:

  严格来讲,.NET中并不存在静态绑定。所有的.NET源文件都首先被编译为IL代码和元数据,在方法执行时,IL代码才被JIT编译器即时转换为本地CPU指令。JIT编译发生于运行时,因此也就不存在完全在编译期建立的关联关系,静态绑定的概念也就无从谈起。

  在此,我们提取万能加载器FileLoader中的部分代码,来深入分析通过虚方法进行动态绑定的一般过程:

  Files myFile = new WORDFile();

  myFile.Open();

  针对上述示例,具体的调用过程,可以小结为:

  编译器首先检查myFile的声明类型为Files,然后查看my-File调用方法是否被实现为虚方法。如果不是虚方法,则直接执行即可;如果是虚方法,则会检查实现类型WORDFile是否重写该方法Open,如果重写则调用WORDFile类中覆写的方法,例如本例中就将执行WORDFile类中覆写过的方法;如果没有重写,则向上递归遍历其父类,查找是否覆写该方法,直到找到第一个覆写方法调用才结束。

  3、多态的规则和意义

  (1)多态提供了对同一类对象的差异化处理方式,实现了对变化和共性的有效封装和继承,体现了“一个接口,多种方法”的思想,使方法抽象机制成为可能。

  (2)在.NET中,默认情况下方法是非虚的,以C#为例必须显式地通过virtual或者abstract标记为虚方法或者抽象方法,以便在子类中覆写父类方法。

  (3)在面向对象的基本要素中,多态和继承、多态和重载存在紧密的联系,正如前文所述多态的基础就是建立有效的继承体系,因此继承和重载是多态的实现基础。