把C#中方法重载说透

简介:
首 先我们说一下什么是方法重载。在面对对象这样的高级语言中都允许我们在一个类中定义多个方法名相同、方法间参数个数和参数顺序不同的方法,对于参数个数不 同或者参数列表不同的情况我们称之为参数列表不同。需要注意的是这里没有提到方法的返回值。也就是决定方法是否构成重载有两个条件:
(1)在同一个类中;
(2)方法名相同;
(3)参数列表不同。
例如下面的代码:
    public void Show()//(1)         {             Console.WriteLine("Nothing");         }
        public void Show(int number)//(2)         {             Console.WriteLine(number);         }         /*         public int Show(int number)//(3)         {             Console.WriteLine(number);             return number % 5;         }         */
 
对于上面的代码,(1)没有参数,(2)使用了一个int类型的参数,(1)和(2)之间就构成了重 载。(2)与(3)相比仅仅返回值不同,虽然重载不关心返回值的不同,但是在C#中不允许存在方法名和参数列表相同、返回值不同的方法,所以(2)和 (3)不能同时存在于代码中,(3)如果不注释掉上面的代码是没有办法通过编译的。
上面我们仅仅讨论了重载的一些基本常识,下面我们探讨一下一些情况稍微复杂的重载情况。
首先我们看第一个版本:
using System; using System.Collections.Generic; using System.Text;
namespace OverrideDemo {     /// <summary>     /// 说明:本实例用于讲述重载的关系     /// 作者:周公     /// 日期:2008-09-04     /// 首发地址:http://blog.csdn.net/zhoufoxcn     /// </summary>     class Program     {         static void Main(string[] args)         {             String s = null;             Show(s);             Object o = "123";             Show(o);         }
        static void Show(string s)         {             Console.WriteLine("String");         }
        static void Show(Object o)         {             Console.WriteLine("Object");         }     } }
大家猜猜这个程序的运行结果是什么?
以下是程序运行结果:
String
Object
对以上代码进行分析,我们发现Show()方法有两种形式,一种是string类型的参数,一种是 object类型参数,在一个类中存在方法名相同、参数列表不同(参数个数或者参数类型不同)的现象我们称之为overloading,即重载。不过这里 的Show()方法的参数比较特殊,因为string类继承于Object类,也就是Show()方法的参数存在一种继承关系。从结果我们可以得出两点结 论:
(1)从String s = null;Show(s);最后调用的是static void Show(string s)这个方法我们可以得出,C#中方法调用是精确匹配的,也就是s是string类型,虽然string类型继承自object类型,尽管static void Show(Object o)也满足条件,但是方法声明中static void Show(string s)这个声明与s类型的最接近(因为s是string类型,与它最接近),所以执行static void Show(string s),而不执行static void Show(Object o)这个方法。
(2)从Object o = "123"; Show(o);最后调用的是static void Show(Object o)这个方法我们可以得出,C#中如果存在方法重载,会根据其refrence type(引用类型)来调用对象的方法,而不是根据instance type(实例类型)来调用。尽管”123”是string类型,但是它的refrence type是object类型的,所以会调用static void Show(Object o)这个方法而不是static void Show(string s)。
上面的Main()方法的IL代码如下:
.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代码大小       24 (0x18)
  .maxstack  1
  .locals init ([0] string s,
           [1] object o)
  IL_0000:  nop
  IL_0001:  ldnull
  IL_0002:  stloc.0
  IL_0003:  ldloc.0
  IL_0004:  call       void OverrideDemo.Program::Show(string)
  IL_0009:  nop
  IL_000a:  ldstr      "123"
  IL_000f:  stloc.1
  IL_0010:  ldloc.1
  IL_0011:  call       void OverrideDemo.Program::Show(object)
  IL_0016:  nop
  IL_0017:  ret
} // end of method Program::Main
从上面的IL代码我们可以看出对于string s=null;这句代码在IL中表示为:ldnull。
再根据上面的结论,我们看下面的代码:
using System; public class Program {   public static void Main()     {     Show(null);     Show("");     Show(1);     }
    static void Show(Object o)     {     Console.WriteLine("Object");     }          static void Show(String s)     {     Console.WriteLine("String");     }      }
猜猜上面的代码执行结果会是怎样的?
以下是程序运行结果:
String
String(感谢btbtshu、windman0925提醒此处的笔误,原来粘贴错了,成了Object了,现在已更正)
Object
从上面的运行结果我们可以得出以下结论:
(1)从Show(null)最后调用的是 static void Show(String s)方法我们更进一步可以说在C#中是方法调用尽量精确匹配的。尽管null我们可以理解为一个空object对象或者一个空字符串,但是在这里C#还是 精确为派生类。这就像我们没有钱,可以说没有一分钱也可以说没有500英镑,但是没有一分钱自然就没有500亿英镑,所以我们跟别人说没有钱的时候没有必 要说没有500亿英镑一样。在这里自然null就表示空字符串。所以Show(null)这个方法会调用static void Show(String s)这个方法。
这有点像下面的情况:
一次活动大会上,主持人说:“身高不到1.60m的请坐在1到3排,身高不到1.75m的请做到4到6排,其他的请随便坐。”
上 面的语句似乎有些逻辑方面的问题,应该说身高超过1.60m但是不到1.75m的请坐到4到6排。但是如果你面对着一群拿着枪的强盗,他说上面的话时,恰 好你也在场并且你的身高是1.55m,你会坐到哪一排?你总不可能冒着挨一枪的危险去纠正他的逻辑错误吧?最好的办法是坐到1到3排。因为无论怎么说你的 身高是绝对满足不到1.60m这个条件的(尽管你的身高也满足强盗说的第二个条件,即身高不到1.75米,但是你肯定不会冒这个危险,从上下句的意思我们 也能推断出人家的意思就是身高在1.61m到1.74m之间的人坐4到6排)。
在上面的代码中,你在运行环境的眼中就是一个持枪的强盗,虽然 null可以理解为null类型的string或者null类型的object,但是它不能向你问清楚这个到底是null类型的string或者null 类型的object,因为string是Object的派生类,所以它按照null类型的string来调用相应的方法了。
(2)从 Show("")最后调用static void Show(String s)这个方法进一步证明了方法调用是尽量选择参数最匹配的那个执行。因为Show("")相当于:string s = ""; Show(s);s的引用类型是string,所以会调用static void Show(String s)这个方法。
我们在这里可以假设一下:假如存在一个类A是String类的派生类(实际上string类是sealed的,也就是不可继承的,所以我说了是假设),并且存在在上面的代码改变如下:
using System; public class Program {   public static void Main()     {     Show(null);     Show("");     }
    static void Show(Object o)     {     Console.WriteLine("Object");     }          static void Show(String s)     {     Console.WriteLine("String");     }
    static void Show(A a)//假设A是String的派生类,当然实际上String类并没有派生类,这里仅仅是假设     {     Console.WriteLine("A");     }      }
如果上面的假设成立,上面的代码运行结果应该如下:
A
String
(3)为什么Show(1)会调用static void Show(Object o)这个方法呢?在这个类中与Show(1)最精确的方法重载应该是static void Show(int i)这种方法声明,但是方法中没有,因为int是继承自ValueType类,所以如果没有static void Show(int i)这种声明,那么其次接近的声明应该是static void Show(ValueType v)这种声明,可惜方法中依然没有,不过ValueType类继承自Object类,所以比static void Show(ValueType v)还次一点的方法重载声明应该是static void Show(Object o),而类中也确实存在这种声明,所以会调用static void Show(Object o)这个方法。当然从int到Object这个过程中存在一次box,也就是装箱(装箱是从值类型到引用类型的转换),这个可以从下面的IL代码可以看出 来。
以下是第二种情况下Main()方法的IL代码:
.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // 代码大小       32 (0x20)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldnull
  IL_0002:  call       void OverrideDemo.Program::Show(string)
  IL_0007:  nop
  IL_0008:  ldstr      ""
  IL_000d:  call       void OverrideDemo.Program::Show(string)
  IL_0012:  nop
  IL_0013:  ldc.i4.1
  IL_0014:  box        [mscorlib]System.Int32
  IL_0019:  call       void OverrideDemo.Program::Show(object)
  IL_001e:  nop
  IL_001f:  ret
} // end of method Program::Main
下面我们对第二种情况的代码做一些变化,代码如下:
using System; using System.Collections.Generic; using System.Text;
namespace OverrideDemo {     /// <summary>     /// 说明:本实例用于讲述重载的关系     /// 作者:周公     /// 日期:2008-09-04     /// 首发地址:http://blog.csdn.net/zhoufoxcn     /// </summary>     class Program     {         static void Main(string[] args)         {             Show(null);             Show("");              Show(1);         }
        static void Show(string s)         {             Console.WriteLine("String");         }
        static void Show(Object o)         {             Console.WriteLine("Object");         }
        static void Show(Program p)//Program是当前方法所在的类         {             Console.WriteLine("Program");         }     } }
 
上面的代码的运行结果是什么,你能猜出来吗?
哈哈,上面的程序代码是没有运行结果的,因为它没有办法编译!编译情况如下:
 
为什么不能通过编译呢?
原 因就出在Show(null)这个方法这里!如果仅仅有static void Show(string s)和static void Show(Object o)方法构成重载关系,那么null我们既可以理解为空string引用也可以理解为空Object引用,因为string类型的限制更精确一些,所以 C#会按照最精确地匹配成string类型,因而会执行static void Show(string s)这个方法。这是在前面的代码中已经被证明的。可是现在多了一个static void Show(Program p)方法的重载,null既可以理解成空string类型引用,也可以理解成空Program类型引用,因为string类和Program类都是 Object类的派生类,所以按照前面的推论自然不会当成空Object类型的引用。因为String类和Program类之间不存在继承关系,按照最精 确匹配原则,编译器无法决定匹配成String类还是Program类最精确,所以编译无法通过。
附注:最近尝试将一些比较基础的理论的知识用比较浅显的话语表达出来,主要是为了方便初学者理解和学 习,也许某些词语用得不够professional,,但是如果词语太professional了怕初学者理解起来有困难,敬请各位大侠谅解,因为本文不 是为你们写的。同时也欢迎各位与我交流经验和心得。最近垃圾邮件太多,所以不便在这里公开我的email,如果各位有兴趣与我交流,请加我为csdn好 友,这样就能看见我的QQ和email了.


















本文转自周金桥51CTO博客,原文链接: http://blog.51cto.com/zhoufoxcn/162948,如需转载请自行联系原作者
相关文章
|
9月前
|
存储 C#
C# 方法详解:定义、调用、参数、默认值、返回值、命名参数、方法重载全解析
方法是一段代码,只有在调用时才会运行。 您可以将数据(称为参数)传递给方法。 方法用于执行某些操作,也被称为函数。 为什么使用方法?为了重用代码:定义一次代码,然后多次使用。
191 0
【C#视频】方法重载、函数重载、传值
【C#视频】方法重载、函数重载、传值
|
编译器 C#
C#的方法重载
C#的方法重载
116 0
|
C#
C#基础知识之方法重载总结
1.首先解释一下什么是方法重载?    方法重载是指在同一个类中方法同名,参数不同,调用时根据实参的形式,选择与他匹配的方法执行操作的一种技术。 这里所说的参数不同是指以下几种情况: ①  参数的类型不同 ②  参数的个数不同 ③  参数的个数相同且类型不同时,它们的先后顺序不同 需要注...
665 0
|
C#
C#方法重载(overload)、重写(覆盖)(override)、隐藏(new)
  重载、重写、隐藏这三个概念对于很多人都不是很清晰,我也是差了很多资料又请教师哥才感觉能理解了,有不足之处还请老师同学们批评指正!   定义:   重载:同一个作用域内发生(比如一个类里面),定义一系列同名方法,但是方法的参数列表不同。
1762 0
|
3月前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
61 3
|
2月前
|
存储 安全 编译器
学懂C#编程:属性(Property)的概念定义及使用详解
通过深入理解和使用C#的属性,可以编写更清晰、简洁和高效的代码,为开发高质量的应用程序奠定基础。
116 12
|
3月前
|
设计模式 C# 图形学
Unity 游戏引擎 C# 编程:一分钟浅谈
本文介绍了在 Unity 游戏开发中使用 C# 的基础知识和常见问题。从 `MonoBehavior` 类的基础用法,到变量和属性的管理,再到空引用异常、资源管理和性能优化等常见问题的解决方法。文章还探讨了单例模式、事件系统和数据持久化等高级话题,旨在帮助开发者避免常见错误,提升游戏开发效率。
115 4