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#程序设计有所帮助。

(0)

相关推荐

  • C#与.net高级编程 C#的多态介绍

    用virtual关键字标记的方法称为虚方法,如果子类希望改变虚方法的实现细节就必须使用 override关键字. 抽象类 abstract关键字 防止创建毫无意义的基本类,使用abstract关键字创建抽象基类,防止被实例化 使用abstract关键字创建抽象方法,强制每一个子类重写特定的方法,abstract成员没有提供任何实现. (注:抽象方法只可以定义在抽象类中,如果不是这样的话,就会收到编译器错误) 成员投影 如果派生类定义的成员和定义在蕨类中的成员一致,派生类投影了父类的版本. 如:我

  • c#继承与多态使用示例

    继承和多态 派生类具有基类所有非私有数据和行为以及新类自己定义的所有其他数据或行为,即子类具有两个有效类型:子类的类型和它继承的基类的类型. 对象可以表示多个类型的能力称为多态性. 多态性示例 复制代码 代码如下: public class Parent    {        public Parent() { }        public void MethodA()        {            Console.WriteLine("调用MethodA()");   

  • C#基础继承和多态详解

    继承 在现有类(称为基类.父类)上建立新类(称为派生类.子类)的处理过程为继承.派生类能自动获取基类(除了构造函数和析构函数外的所有成员),可以在派生类中添加新的属性和方法扩展其功能. 复制代码 代码如下: using System;using System.Collections.Generic;using System.Linq;using System.Web; public class Person{ private string _id;    public string id   

  • c#基础学习之多态

    最近在看一本书<你必须知道的.Net>,书涵盖的内容比较多,对于c#,.Net平台以往所学的零散东西有了慢慢的总结和新的认识.把一些基础的东西记录于此. 先说说多态吧: 1.基类继承式多态 如书中所说,基类继承式多态的关键是继承体系的设计与实现.书中举了个简单的列子 复制代码 代码如下: Files myFile=new WORDFile(); myFile.open(); myFile是一个父类Files变量,保持了指向子类WORDFile实例的引用,然后调用一个虚方法Open,具体的调用则

  • C#中多态、重载、重写区别分析

    本文实例总结了C#中多态.重载与重写的概念与区别.对于初学C#的朋友来说有不错的参考价值.分享给大家供大家参考.具体分析如下: 重写:是指重写基类的方法,在基类中的方法必须有修饰符virtual,而在子类的方法中必须指明override. 格式如下: 1.在基类中: public virtual void myMethod() { } 2.在子类中: public override void myMethod() { } 重写以后,用基类对象和子类对象访问myMethod()方法,结果都是访问在

  • C#使用虚拟方法实现多态

    本文实例讲述了C#使用虚拟方法实现多态.分享给大家供大家参考.具体分析如下: 我们看一个例子,假若有一个动物类,类中用方法cry()描述动物的叫声,不同的动物的叫声是不一样的.根据继承的特征,把类中公共部分的内容放在父类中,那么cry()方法就应该放在父类中,根据这样的思路编写程序如下: using System; class Anmial { public void Cry() { Console.WriteLine("这是动物的叫声"); } } class Dog: Anmial

  • C# 多态性的深入理解

    MSDN 上面的定义:通过继承,一个类可以有多种类型:可以用作它自己的类型,任何基类型,或者在实现接口时用作任何接口的类型.从两个方面来说明多态1.在运行时,方法参数和集合或者是数组等位置,派生类的对象都可以作为基类的对象处理,发生此情况时,该对象的声明类型不再与运行时类型相同.2.基类定义实现虚方法,派生类重写这些方法,在运行时,CLR会查找运行时类型,并且调用派生类重写的方法. 复制代码 代码如下: class Shape    {        public virtual void Dr

  • C#中的多态深入理解

    封装.继承.多态,面向对象的三大特性,前两项理解相对容易,但要理解多态,特别是深入的了解,对于初学者而言可能就会有一定困难了.我一直认为学习OO的最好方法就是结合实践,封装.继承在实际工作中的应用随处可见,但多态呢?也许未必,可能不经意间用到也不会把它跟"多态"这个词对应起来.在此抛砖引玉,大家讨论,个人能力有限,不足之处还请指正. 之前看到过类似的问题:如果面试时主考官要求你用一句话来描述多态,尽可能的精炼,你会怎么回答?当然答案有很多,每个人的理解和表达不尽相同,但我比较趋向这样描

  • C#中面向对象编程机制之多态学习笔记

    C#的多态性: 我的理解是:同一个操作,作用于不同的对象时,会有不同的结果,即同一个方法根据需要,作用于不同的对象时,会有不同的实现. C#的多态包括:接口多态,继承多态. 其中继承多态又包括通过虚拟方法实现的多态和通过抽象方法实现的多态性 例如:基类动物都有吃的方法,但是不同的动物吃的东西就会不一样,例如狼吃肉,羊吃草,这样"吃"的这个方法就要在派生类里面重新实现以下,运行时,通过指向基类的指针,来调用实现派生类中的方法. 接下来举例实现多态性. 1. 接口多态性 把动物"

  • C#中多态现象和多态的实现方法

    本文实例讲述了C#中多态现象和多态的实现方法.分享给大家供大家参考.具体分析如下: 面向对象的特征封装.继承和多态.Polymorphism(多态性)来源于希腊单词,指"多种形态".多态性的一个重要特征是方法的调用是在运行时确定而不是编译时.在.NET中用于实现多态性的关键词有virtual.override.abstract.interface. 一.virtual实现多态   shape类是通用的基类,draw是一个虚方法,每个派生类都可以有自己的override版本,在运行时可以

  • C# 中的多态底层虚方法调用详情

    目录 一.C# 中的多态玩法 1. 一个简单的 C# 例子 2. 汇编代码分析 (1)eax,dword ptr [ebp-8] (2)eax,dword ptr [eax] (3)eax,dword ptr [eax+28h] (4)call dword ptr [eax+10h] 三.总结 前言: 本质上来说,CoreCLR 也是 C++ 写的,所以也逃不过用 虚表 来实现多态的玩法, 不过玩法也稍微复杂了一些,希望本篇对大家有帮助. 一.C# 中的多态玩法 1. 一个简单的 C# 例子 为

  • C++中继承与多态的基础虚函数类详解

    前言 本文主要给大家介绍了关于C++中继承与多态的基础虚函数类的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 虚函数类 继承中我们经常提到虚拟继承,现在我们来探究这种的虚函数,虚函数类的成员函数前面加virtual关键字,则这个成员函数称为虚函数,不要小看这个虚函数,他可以解决继承中许多棘手的问题,而对于多态那他更重要了,没有它就没有多态,所以这个知识点非常重要,以及后面介绍的虚函数表都极其重要,一定要认真的理解~ 现在开始概念虚函数就又引出一个概念,那就是重写(覆

  • Java中继承、多态、重载和重写介绍

    什么是多态?它的实现机制是什么呢?重载和重写的区别在那里?这就是这一次我们要回顾的四个十分重要的概念:继承.多态.重载和重写. 继承(inheritance) 简单的说,继承就是在一个现有类型的基础上,通过增加新的方法或者重定义已有方法(下面会讲到,这种方式叫重写)的方式,产生一个新的类型.继承是面向对象的三个基本特征--封装.继承.多态的其中之一,我们在使用JAVA时编写的每一个类都是在继承,因为在JAVA语言中,java.lang.Object类是所有类最根本的基类(或者叫父类.超类),如果

  • c++ 构造函数中调用虚函数的实现方法

    我们知道:C++中的多态使得可以根据对象的真实类型(动态类型)调用不同的虚函数.这种调用都是对象已经构建完成的情况.那如果在构造函数中调用虚函数,会怎么样呢? 有这么一段代码: class A { public: A ():m_iVal(0){test();} virtual void func() { std::cout<<m_iVal<<' ';} void test(){func();} public: int m_iVal; }; class B : public A {

  • 基于多态之虚方法、抽象类、接口详解

    虚方法: 1.在父类方法的返回值前加 virtual 关键字,标记为虚方法,表示这个方法可以被子类重写. 2.虚方法必须有方法体,方法体中可以没有任何内容. 3.子类可以根据需求选择性的是否重写虚方法.如果需要重写,在子类方法的返回值前加 override 关键字. 4.子类在重写虚方法时,可以根据需求选择性的是否使用 base 关键字调用父类中的该方法. 虚方法语法格式如下: public class Father { public virtual void Do() { //..... }

  • .NET Core WebApi中如何实现多态数据绑定实例代码

    什么是.NET Core? 随着2014年 Xamarin和微软发起.NET基金会,微软在2014年11月份 开放.NET框架源代码.在.NET开源基金会的统一规划下诞生了.NET Core .也就是说.NET Core Framework是参考.NET Framework重新开发的.NET实现,Mono是.NET Framework的一个开源的.跨平台的实现. 本文主要介绍了关于.NET Core WebApi多态数据绑定的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍

  • 详解C++值多态中的传统多态与类型擦除

    引言 我有一个显示屏模块: 模块上有一个128*64的单色显示屏,一个单片机(B)控制它显示的内容.单片机的I²C总线通过四边上的排针排母连接到其他单片机(A)上,A给B发送指令,B绘图. B可以向屏幕逐字节发送显示数据,但是不能读取,所以程序中必须设置显存.一帧需要1024字节,但是单片机B只有512字节内存,其中只有256字节可以分配为显存.解决这个问题的方法是在B的程序中把显示屏分成4个区域,保存所有要绘制的图形的信息,每次在256字节中绘制1/4屏,分批绘制.发送. 简而言之,我需要维护

  • python 中Mixin混入类的使用方法详解

    目录 前言 Mixin 与继承的区别 总结 前言 最近在看sanic的源码,发现有很多Mixin的类,大概长成这个样子 class BaseSanic(    RouteMixin,    MiddlewareMixin,    ListenerMixin,    ExceptionMixin,    SignalMixin,    metaclass=SanicMeta, ): 于是对于这种 Mixin 研究了一下,其实也没什么新的东西,Mixin 又称混入,只是一种编程思想的体现,但是在使用

  • JavaScript实现二叉树的先序、中序及后序遍历方法详解

    本文实例讲述了JavaScript实现二叉树的先序.中序及后序遍历方法.分享给大家供大家参考,具体如下: 之前学数据结构的时候,学了二叉树的先序.中序.后序遍历的方法,并用C语言实现了,下文是用js实现二叉树的3种遍历,并以动画的形式展现出遍历的过程. 整个遍历过程还是采用递归的思想,原理很粗暴也很简单 先序遍历的函数: function preOrder(node){ if(!(node==null)){ divList.push(node); preOrder(node.firstEleme

随机推荐