本文将介绍以下内容:

  • 什么是覆写,什么是重载
  • 覆写与重载的区别
  • 覆写与重载在多态特性中的应用

1. 引言

覆写(override)与重载(overload),是成就.NET面向对象多态特性的基本技术之一,两个貌似相似而实则不然的概念,常常带给我们很多的误解,因此有必要以专题来讨论清楚其区别,而更重要的是关注其在多态中的应用。

在系列中,我们先后都有关于这一话题的点滴论述,本文以专题的形式再次做以深度讨论,相关的内容请对前文做以参考。

2. 认识覆写和重载

从一个示例开始来认识什么是覆写,什么是重载?

    abstract class  Base
    {
        //定义虚方法
        public virtual void MyFunc()
        { 
        }

//参数列表不同,virtual不足以区分
        public virtual void MyFunc(string str)
        {
        }

//参数列表不同,返回值不同
        public bool MyFunc(string str, int id)
        {
            Console.WriteLine("AAA");
            return true;
        }

//参数列表不同表现为个数不同,或者相同位置的参数类型不同 
        public bool MyFunc(int id, string str)
        {
            Console.WriteLine("BBB");
            return false;
        }

//泛型重载,允许参数列表相同
        public bool MyFunc<T>(string str, int id)
        {
            return true;
        }

//定义抽象方法
        public abstract void Func();
    }

class Derived: Base
    {
        //阻隔父类成员
        public new void MyFunc()
        {
        }

//覆写基类成员
        public override void MyFunc(string str)
        {
            //在子类中访问父类成员
            base.MyFunc(str);
        }

//覆写基类抽象方法
        public override void Func()
        {
            //实现覆写方法
        }
}

2.1 覆写基础篇

覆写,又称重写,就是在子类中重复定义父类方法,提供不同实现,存在于有继承关系的父子关系。当子类重写父类的虚函数后,父类对象就可以根据根据赋予它的不同子类指针动态的调用子类的方法。从示例的分析,总结覆写的基本特征包括:

  • 在.NET中只有以virtual和abstract标记的虚方法和抽象方法才能被直接覆写。
  • 覆写以关键字override标记,强调继承关系中对基类方法的重写。
  • 覆写方法要求具有相同的方法签名,包括:相同的方法名、相同的参数列表和相同的返回值类型。
概念:虚方法

虚方法就是以virtual关键字修饰并在一个或多个派生类中实现的方法,子类重写的虚方法则以override关键字标记。虚方法调用,是在运行时确定根据其调用对象的类型来确定调用适当的覆写方法。.NET默认是非虚方法,如果一个方法被virtual标记,则不可再被static、abstrcat和override修饰。

概念:抽象方法

抽象方法就是以abstract关键字修饰的方法,抽象方法可以看作是没有实现体的虚方法,并且必须在派生类中被覆写,如果一个类包括抽象方法,则该类就是一个抽象类。因此,抽象方法其实隐含为虚方法,只是在声明和调用语法上有所不同。abstract和virtual一起使用是错误的。

2.2 重载基础篇

重载,就是在同一个类中存在多个同名的方法,而这些方法的参数列表和返回值类型不同。值得注意的是,重载的概念并非面向对象编程的范畴,从编译器角度理解,不同的参数列表、不同的返回值类型,就意味着不同的方法名。也就是说,方法的地址,在编译期就已经确定,是这一种静态绑定。从示例中,我们总结重载的基本特征包括:

  • 重载存在于同一个类中。
  • 重载方法要求具有相同的方法名,不同的参数列表,返回值类型可以相同也可以不同(通过operator implicit 可以实现一定程度的返回值重载,不过不值得推荐)。
  • .NET 2.0引入泛型技术,使得相同的参数列表、相同的返回值类型的情况也可以构成重载。

3. 在多态中的应用

多态性,简单的说就是“一个接口,多个方法”,具体表现为相同的方法签名代表不同的方法实现,同一操作作用于不同的对象,产生不同的执行结果。在.NET中,覆写实现了运行时的多态性,而重载实现了编译时的多态性。

运行时的多态性,又称为动态联编,通过虚方法的动态调度,在运行时根据实际的调用实例类型决定调用的方法实现,从而产生不同的执行结果。

    class  Base
    {
        public virtual void MyFunc(string str)
        {
            Console.WriteLine("{0} in Base", str);
        }
    }

class Derived: Base
    {
        //覆写基类成员
        public override void MyFunc(string str)
        {
            Console.WriteLine("{0} in Derived", str);
        }

public static void Main()
        {
            Base B = new Base();
            B.MyFunc("Hello");
            Derived A = new Derived();
            B = A;
            B.MyFunc("Morning");
        }
    }

从结果中可知,对象B两次执行B.MyFunc调用了不同的方法,第一次调用基类方法MyFunc,而第二次调用了派生类方法MyFunc。在执行过程中,对象B先后指向了不同的类的实例,从而动态调用了不同的实例方法,显然这一执行操作并非确定于编译时,而是在运行时根据对象B执行的不同类型来确定的。我们在此不分析虚拟方法的动态调度机制,而只关注通过虚方法覆写而实现的多态特性,详细的实现机制请参考本系列的其它内容。

编译时的多态性,又称为静态联编,一般包括方法重载和运算符重载。对于非虚方法来说,在编译时通过方法的参数列表和返回值类型决定不同操作,实现编译时的多态性。例如,在实际的开发过程中,.NET开发工具Visual Studio的智能感知功能就很好的为方法重载提供了很好的交互手段,例如:

从智能感知中可知方法MyFunc在派生类Derived中有三次重载,调用哪种方法由程序开发者根据其参数、返回值的不同而决定。由此可见,方法重载是一种编译时的多态,对象A调用哪种方法在编译时就已经确定。

4. 比较,还是规则

  • 如果基访问引用的是一个抽象方法,则将导致编译错误。
    abstract class  Base
    {
        public abstract void Func();
    }

class Derived: Base
    {
        //覆写基类抽象方法
        public override void Func()
        {
            base.Func();
        }
    }

  • 虚方法不能是静态的、密封的。
  • 覆写实现的多态确定于运行时,因此更加的灵活和抽象;重载实现的多态确定于编译时,因此更加的简单和高效。二者各有特点与应用,不可替代。

在下表中,将覆写与重载做以总结性的对比,主要包括:

规则

覆写(override

重载(overload

存在位置

存在于有继承关系的不同类中

存在于同一个类中

调用机制

运行时确定

编译时确定

方法名

必须相同

必须相同

参数列表

必须相同

必须不同

返回值类型

必须相同

可以不相同

泛型方法

可以覆写

可以重载

注:参数列表相同表示参数的个数相同,并且相同位置的参数类型也相同。

5. 结论

深入的理解覆写和重载,是对多态特性和面向对象机制的有力补充,本文从基本概念到应用领域将两个概念进行一一梳理,通过对比整理区别,还覆写和重载以更全面的认知角度,同时也更能从侧面深入的了解运行时多态与编译时多态的不同情况。

参考文献

(web)TerryLee, 再谈重载与覆写http://www.cnblogs.com/Terrylee/archive/2006/03/10/347104.html

(web)失落的BLOGSC#泛型http://www.cnblogs.com/lianyonglove/archive/2007/07/27/720682.html

转载自http://www.cnblogs.com/anytao/archive/2007/11/07/must_net_17.html

[你必须知道的.NET]第十七回:貌合神离:覆写和重载的更多相关文章

  1. [你必须知道的.NET]第二十七回:interface到底继承于object吗?

    发布日期:2009.03.05 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 在.NET世界里,我们常常听到的一句话莫过于“S ...

  2. [你必须知道的.NET] 第八回:品味类型---值类型与引用类型(上)-内存有理

    原文地址:http://kb.cnblogs.com/page/42318/ 系列文章导航: [你必须知道的.NET] 开篇有益 [你必须知道的.NET] 第一回:恩怨情仇:is和as [你必须知道的 ...

  3. [你必须知道的.NET]第二十一回:认识全面的null

    发布日期:2008.7.31 作者:Anytao © 2008 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 null.nullable.??运算符.null ...

  4. [你必须知道的.NET]第二十回:学习方法论

    说在,开篇之前 本文,源自我回答刚毕业朋友关于.NET学习疑惑的回复邮件. 本文,其实早计划在<你必须知道的.NET>写作之初的后记部分,但是因为个中原因未能如愿,算是补上本书的遗憾之一. ...

  5. [你必须知道的.NET]第二十三回:品味细节,深入.NET的类型构造器

    发布日期:2008.11.2 作者:Anytao © 2008 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 今天Artech兄在<关于Type Init ...

  6. [你必须知道的.NET]第二十四回:认识元数据和IL(上)

    发布日期:2009.02.24 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 很早就有说说Metadata(元数据)和IL(中 ...

  7. [你必须知道的.NET]第三十一回,深入.NET 4.0之,从“新”展望

    发布日期:2009.05.22 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. /// <summary> /// 本文开始,将以& ...

  8. [你必须知道的.NET]第二十六回:认识元数据和IL(下)

    发布日期:2009.03.04 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回: 第二十四回:认识元数据和IL(上), ...

  9. [你必须知道的.NET]第二十五回:认识元数据和IL(中)

    发布日期:2009.02.25 作者:Anytao © 2009 Anytao.com ,Anytao原创作品,转贴请注明作者和出处. 说在,开篇之前 书接上回[第二十四回:认识元数据和IL(上)], ...

随机推荐

  1. vs2003一查找就卡死了

    Visual Studio 2003一查找就卡死了.解决办法如下: win7 32位下 解决方法:找到VS2003的安装目录,修改"...\Microsoft Visual Studio . ...

  2. ctsc2009 移民站选址

    分析:非常非常好的一道题! 首先需要对问题进行转化: 行列无关,对于行单独处理,对于列单独处理 必然存在一个最优方案使得每一个新站与旧站重合. 转化1很显然,对于转化2,是一类非常经典的“中位数问题” ...

  3. 对于redis框架的理解(三)

    上一篇讲完了initServer的大体流程,其中aeCreateEventLoop(),这个函数 没有详细说明,我们在这一篇里讲述Ae.h和Ae.c, 这里面的api阐述了如何创建 eventLoop ...

  4. Unicode/UTF-8/GBK/ASCII 编码简介

    转载:http://blog.csdn.net/u014785687/article/details/73928167 一.字符编码简介 1.ASCII编码 每一个ASCII码与一个8位(bit)二进 ...

  5. Elasticsearch技术解析与实战(七)Elasticsearch partial update

    普通的partial update 1.插入测试数据 PUT /test_index/test_type/10 { "test_field1": "test1" ...

  6. datagrid导出数据

    //导出excel public function touzi_doExport() { $search=$_POST['search']; //接受前端传过来的数据 $this->succes ...

  7. 树的直径(两个bfs)

    题目链接:https://cn.vjudge.net/problem/POJ-2631 树的直径:树中的最长链 具体思路:随便找一个点bfs,然后找到最长的链,然后再以找到的点作为起点进行bfs,然后 ...

  8. 南邮综合题writeup

    http://teamxlc.sinaapp.com/web3/b0b0ad119f425408fc3d45253137d33d/index.php fuckjs直接console得到地址 http: ...

  9. imperva系统升级遇见的错误(配置文件的导入导出)

    今天心态有点炸了 今天去东兴证券做waf升级.浪费了两天才弄完.把客户都弄得有点急了.好歹原厂的工程师耐心的讲解这才弄完.感谢路哥.... 赶紧总结一下. 事情是这样的.东兴 证券的imperva是v ...

  10. linux下C语言实现的哈希链表【转】

    转自:http://blog.chinaunix.net/uid-28458801-id-4276934.html 操作系统:ubuntu10.04 前言:     在稍微大点的项目中,基本都会遇到算 ...