面向对象的特征封装、继承和多态。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.ReadLine();

/*运行结果

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 void Draw()

{

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 in shapes)

{

s.Draw();

}

Console.ReadLine();

/*运行结果

Drawing a Rectangle

Drawing a Square

Drawing a Rectangle

*/

}

abstract实现多态

被abstract修饰的方法,默认是虚拟的,但是不能出现virtual关键词修饰。被abstract修饰的类可以有已实现的成员,可以有自己的字段,可以有非abstract 修饰的方法,但是不能实例化因为抽象的东西是没有实例对应的。比如,有人让我们画个图形(抽象)是画不出来的,但是让画个矩形(具体)是可以画出来的。下面是用abstract实现的多态版本,

public abstract class Shape

{

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.ReadLine();

}

}

被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 in shapes)

{

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 new virtual 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());

}

}

class Program

{

static void Main(string[] args)

{

int val = 5;

Derived d = new Derived();

d.Write(val);

Console.ReadLine();

}

}

/*运行结果:

derived double:5

*/

在 Derived 的一个实例中调用 Write时,C# 编译器将首先尝试使该调用与最初在 Derived 上声明的 Write版本兼容。重写方法不被视为是在类上进行声明的,而是在基类上声明的方法的新实现。由于变量val可以隐式的转换为double型的,所以C#编译器选择Write(double num)。仅当 C# 编译器无法将方法调用与 Derived 上的原始方法匹配时,它才尝试将该调用与具有相同名称和兼容参数的重写方法匹配。

C#中的多态现象的更多相关文章

  1. JavaSE自学笔记

    ch03 [Thu Aug 18 2016 11:22:26 GMT+0800] 对象变量与对象之间是指代关系,对象变量并不能完全说明有无对象可用.这种指代关系是通过赋值运算建立起来的.对象变量保存的 ...

  2. Python开源框架

    info:更多Django信息url:https://www.oschina.net/p/djangodetail: Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC) ...

  3. 深入Java核心 Java中多态的实现机制(1)

    在疯狂java中,多态是这样解释的: 多态:相同类型的变量,调用同一个方法时,呈现出多中不同的行为特征, 这就是多态. 加上下面的解释:(多态四小类:强制的,重载的,参数的和包含的) 同时, 还用人这 ...

  4. java中abstract

    abstract(抽象)修饰符,可以修饰类和方法 1,abstract修饰类,会使这个类成为一个抽象类,这个类将不能生成对象实例,但可以做为对象变量声明的类型,也就是编译时类型,抽象类就像当于一类的半 ...

  5. java中实现多态的机制是什么?

    多态性是面向对象程序设计代码重用的一个重要机制,我们曾不只一次的提到Java多态性.在Java运行时多态性:继承和接口的实现一文中,我们曾详细介绍了Java实现运行时多态性的动态方法调度:今天我们再次 ...

  6. C++中多态性学习(上)

    多态性学习(上) 什么是多态? 多态是指同样的消息被不同类型的对象接收时导致不同的行为.所谓消息是指对类的成员函数的调用,不同的行为是指不同的实现,也就是调用了不同的函数.虽然这看上去好像很高级的样子 ...

  7. (C/C++学习)5.C++中的虚继承-虚函数-多态解析

    说明:在C++学习的过程中,虚继承-虚函数经常是初学者容易产生误解的两个概念,它们与C++中多态形成的关系,也是很多初学者经常产生困惑的地方,这篇文章将依次分别对三者进行解析,并讲述其之间的联系与不同 ...

  8. C++中的纯虚函数

    ---恢复内容开始--- 在C++中的一种函数申明被称之为:纯虚函数(pure virtual function).它的申明格式如下 class CShape { public: ; }; 在什么情况 ...

  9. java中abstract怎么使用

    abstract(抽象)修饰符,可以修饰类和方法 1,abstract修饰类,会使这个类成为一个抽象类,这个类将不能生成对象实例,但可以做为对象变量声明的类型,也就是编译时类型,抽象类就像当于一类的半 ...

随机推荐

  1. Openstack之Swift架构(Cloud Storage)

    Swift是OpenStack的子项目之一,也称为对象储存,适用于储存永久类型的静态数据,例如:虚拟机镜像文件.图片.存档备份等 复制的三个副本如何联系在一起? 让我们用一些具体场景和介绍一些组件,来 ...

  2. MSSQL 2005数据库可疑状态

    今天早上打开进销存,提示链接失败,经过检查参数,网络.端口等各种情况,均没有发现问题,最后检查数据库本事的问题. 通过studio进去发现我的进销存数据变成了(可疑)状态,随机百度修复方法,修复方法还 ...

  3. 摄影初学者挑选相机的常见问题 FAQ

    数码相机一次次降价,越来越多的人加入摄影的行列,照相器材还是一个比较专业的领域,并非简单的参数比一下高低就可以知道好坏,很多朋友往往了解了好久还没弄清孰优孰劣,在购机前踌躇半天拿不定主意,我收集了被问 ...

  4. oracle出现的小问题

    oracle无法解析指定的标识符:检查oracle的数据库实例名是否正确. oracle增大db_files:alter system set db_files=1000 scope=spfile;之 ...

  5. 待整理 - BAT文件编写

    BAT文件语法和技巧(bat文件的编写及使用)  http://www.jb51.net/article/5828.htm

  6. javaScript 获取主机地址,项目名等

    //获取当前网址  var curWwwPath=window.document.location.href; alert(curWwwPath);  //http://localhost:8080/ ...

  7. MON166 User's Guide

    MON166 is a debug monitor for C16x and ST10 user programs. It consists of: A configurable monitor pr ...

  8. java substring和substr

    1.substring 方法 定义和用法 substring 方法用于提取字符串中介于两个指定下标之间的字符. 语法 stringObject.substring(start,stop) 参数     ...

  9. c# 轻量级 ORM 框架 之 Model解析 (四)

    关于orm框架设计,还有必要说的或许就是Model解析了,也是重要的一个环节,在实现上还是相对比较简单的. Model解析,主要用到的技术是反射了,即:把类的属性与表的字段做映射. 把自己的设计及实现 ...

  10. Immutable.js尝试(node.js勿入)

    最近做一些复杂html常常需要在页面做一些数据处理,常常在想如果 js有list 这种数据结构多少,今天逛github时 发现有Immutable.js 这个项目https://github.com/ ...