C#中多态现象和多态的实现方法
本文实例讲述了C#中多态现象和多态的实现方法。分享给大家供大家参考。具体分析如下:
面向对象的特征封装、继承和多态。Polymorphism(多态性)来源于希腊单词,指“多种形态”。多态性的一个重要特征是方法的调用是在运行时确定而不是编译时。在.NET中用于实现多态性的关键词有virtual、override、abstract、interface。
一、virtual实现多态
shape类是通用的基类,draw是一个虚方法,每个派生类都可以有自己的override版本,在运行时可以用shape类的变量动态的调用draw方法。
public class Shape { public virtual void Draw() { Console.WriteLine("base class drawing"); } } public class Rectangle :Shape { public override void Draw() { Console.WriteLine("Drawing a Rectangle"); } } public class Square :Rectangle { public override void Draw() { Console.WriteLine("Drawing a Square"); base.Draw(); } } class Program { static void Main(string[]args) { System.Collections.Generic.List<Shape> shapes =new List<Shape>(); shapes.Add(new Rectangle()); shapes.Add(new Square()); foreach(Shape s in shapes) { s.Draw(); } Console.ReadKey(); /*运行结果 Drawing a Rectangle Drawing a Square Drawing a Rectangle */ } }
方法、属性、事件、索引器都可以被virtual修饰,但是字段不可以。派生类必须用override表示类成员参与虚调用。假如把Square中的draw方法替换为用new 修饰,则表示draw方法不参与虚调用,而且是一个新的方法,只是名字和基类方法重名。
public new void Draw() { Console.WriteLine("Drawing a Square"); base.Draw(); }
这个方法在Main方法中的foreach中将不会被调用,它不是虚方法了。用new修饰符后的程序运行结果,
/*运行结果 Drawing a Rectangle Drawing a Rectangle */
假如说虚方法在rectangle扩展后,不希望square扩展了,可以在方法前加上sealed修饰符,
如下
public class Rectangle :Shape { public sealed override voidDraw() { Console.WriteLine("Drawing a Rectangle"); } }
当派生类重写某个虚拟成员时,即使该派生类的实例被当作基类的实例访问或者把派生类实例赋给父类变量进行访问,但是还是会调用派生类重写后的成员,可以把代码改为如下形式,
static void Main(string[] args) { System.Collections.Generic.List<Shape>shapes =new List<Shape>(); shapes.Add((Shape)new Rectangle()); shapes.Add((Shape)new Square()); foreach(Shape s inshapes) { s.Draw(); } Console.ReadKey(); /*运行结果 Drawing a Rectangle Drawing a Square Drawing a Rectangle */ }
二、abstract实现多态
被abstract修饰的方法,默认是虚拟的,但是不能出现virtual关键词修饰。被abstract修饰的类可以有已实现的成员,可以有自己的字段,可以有非abstract 修饰的方法,但是不能实例化因为抽象的东西是没有实例对应的。比如,有人让我们画个图形(抽象)是画不出来的,但是让画个矩形(具体)是可以画出来的。下面是用abstract实现的多态版本,
public abstract classShape { public abstract void Draw(); } public class Rectangle :Shape { public override void Draw() { Console.WriteLine("Drawing a Rectangle"); } } public class Square :Rectangle { public override void Draw() { Console.WriteLine("Drawing a Square"); base.Draw(); } } class Program { static void Main(string[]args) { System.Collections.Generic.List<Shape>shapes =new List<Shape>(); shapes.Add(new Rectangle()); shapes.Add(new Square()); foreach(Shape s in shapes) { s.Draw(); } Console.ReadKey(); } }
被abstract修饰的方法,在派生类中同样用override关键词进行扩展。同样可以用关键词sealed阻止派生类进行扩展。
interface实现多态
接口可由方法、属性、事件、索引器或这四种成员类型的任何组合构成。接口不能包含字段。接口成员默认是公共的,抽象的,虚拟的。若要实现接口成员,类中的对应成员必须是公共的、非静态的,并且与接口成员具有相同的名称和签名。下面是interface实现的多态版本
public interface IShape { void Draw(); } public class Rectangle :IShape { public void Draw() { Console.WriteLine("Drawing a Rectangle"); } } public class Square: IShape { public void Draw() { Console.WriteLine("Drawing a Square"); } } class Program { static void Main(string[]args) { System.Collections.Generic.List<IShape>shapes =new List<IShape>(); shapes.Add(new Rectangle()); shapes.Add(new Square()); foreach(IShape s inshapes) { s.Draw(); } Console.ReadLine(); } }
抽象类与接口
类可以实现无限个接口,但仅能从一个抽象(或任何其他类型)类继承。从抽象类派生的类仍可实现接口。msdn的在接口和抽象类的选择方面给的一些建议,
如果预计要创建组件的多个版本,则创建抽象类。抽象类提供简单易行的方法来控制组件版本。通过更新基类,所有继承类都随更改自动更新。另一方面,接口一旦创建就不能更改。如果需要接口的新版本,必须创建一个全新的接口。
如果创建的功能将在大范围的全异对象间使用,则使用接口。抽象类应主要用于关系密切的对象,而接口最适合为不相关的类提供通用功能。
如果要设计小而简练的功能块,则使用接口。如果要设计大的功能单元,则使用抽象类。
如果要在组件的所有实现间提供通用的已实现功能,则使用抽象类。抽象类允许部分实现类,而接口不包含任何成员的实现。
一个综合性的实例
public interface IShape { void Draw(); } public class Shape:IShape { void IShape.Draw() { Console.WriteLine("Shape IShape.Draw()"); } public virtual void Draw() { Console.WriteLine("Shape virtual Draw()"); } } public class Rectangle :Shape,IShape { void IShape.Draw() { Console.WriteLine("Rectangle IShape.Draw()"); } public newvirtual void Draw() { Console.WriteLine("Rectangle virtual Draw()"); } } public class Square :Rectangle { public override void Draw() { Console.WriteLine("Square override Draw()"); } } class Program { static void Main(string[]args) { Square squre = new Square(); Rectangle rect = squre; Shape shape = squre; IShape ishape = squre; squre.Draw(); rect.Draw(); shape.Draw(); ishape.Draw(); Console.ReadLine(); } } /*运行结果: Square override Draw()① Square override Draw()② Shape virtual Draw()③ Rectangle IShape.Draw()④ */
在这个程序里,把派生类实例赋给父类变量或者接口。对结果①无需解释。结果②,因为Draw方法是虚方法,虚方法的调用规则是调用离实例变量最近的override版本方法,Square类中的Draw方法是离实例square最近的方法,即使是把Square类型的实例赋值给Rectangle类型的变量去访问,仍然调用的是Square类重写的方法。对于结果③,也是虚方法调用,在子类Rectangle中的draw方法用new修饰,这就表明shape类中的virtual到此中断,后面Square中的override版是针对Rectangle中的Draw方法,此时,离square实例最近的实现就是Shape类中的Draw 方法,因为Shape类中的Draw方法没有override的版本只能调用本身的virtual版了。结果④,因为Rectangle重新声明实现接口IShape,接口调用同样符合虚方法调用规则,调用离它最近的实现,Rectangle中的实现比Shape中的实现离实例square更近。Rectangle中的IShape.Draw()方法是显式接口方法实现,对于它不能有任何的访问修饰符,只能通过接口变量访问它,同时也不能用virtual或者override进行修饰,也不能被派生类型调用。只能用IShape变量进行访问。如果类型中有显式接口的实现,而且用的是接口变量,默认调用显式接口的实现方法。
override和方法选择
public class Base { public virtual void Write(int num) { Console.WriteLine("int:" + num.ToString()); } } public class Derived :Base { public override void Write(int num) { Console.WriteLine("derived:" + num.ToString()); } public void Write(double num) { Console.WriteLine("derived double:" + num.ToString()); } }
希望本文所述对大家的C#程序设计有所帮助。
相关文章
- 我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
- 这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
- 这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
- 本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
- 这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
- 本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)
这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25- 这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
- 这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
- 最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
- 这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
- 本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
- 轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
- 本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
- 这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
- 本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
- 这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
- 下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
- 这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25