运算符重载方法,扩展方法,以及方法参数

运算符重载方法

一 些编程语言允许定义运算符如果操作类型的实例,例如System.String,System.Decimal,和System.DateTime,它们 重载了==和!=运算符。但是对于CLR来说,它是完全不知道像“==”和“!=”这些运算符是干什么的。编程语言定义了每一种运算符的意义以及当这些运 算符的符号出现时应该生成什么样的代码。例如C#里面,数值类型的当遇到“+”这个符号时,在编译时会生成将两个number加起来的代码,如果是 String类型遇到,则会将两个字符串拼接起来。

CLR指定运算符重载方法必须是publicstatic,并且C#要求运算符方法的参数中至少有一个参数的类型跟运算符方法定义的类型一致。这样做的原因是:它能够让C#编译器在一个合理的时间期限找到该运算符绑定的方法。例如:

//定义个类,并在类里面重载+运算符
 public class Complex
 {
 public static Complex operator +(int i, int j)
 {
 ...
 }
 }

这样写会报这样的错误:元运算符的参数之一必须是包含类型,修正如下:

 public class Complex
 {
 public static Complex operator +(Complex i, int j)
 {
 ...
 }
 }

编译器会生成定义了入口的op_Addition方法,这个方法也具有specialname标 志,表明它是一个特殊的方法。当编译器发现了“+”运算符时,它会去检查是否存在一个标记了specialname的op_Addition方法的参数的 类型跟操作数的类型兼容。如果存在这样的方法,编译器会生成调用该方法的代码,如果不存在,则报错。

下面展示了C#中一元和二元运算符对应的CLS方法名:

运算符重载是非常有用的工具,可以让开发者用简洁的代码来表达思想。然而,并不是所有的编程语言都支持运算符重载。当使用不支持运算符重载的语言时,编译器会报错。此时应该允许直接调用如op_Addition这样的方法。在C#里面,不能直接调用op_Addition方法。当C#编译器检测到“+”运算符时,会寻找具有sepecialname标记的op_Addition方法。

扩展方法

扩展方法提供了这样一种调用方式,那就是使用实例方法的调用语法来调用一个静态方法。定义扩展方法的语法是对扩展的类型使用this关键字。如:

public static class StringBuilderExtensions { 
 public static Int32 IndexOf( this StringBuilder sb, Char value) { 
 for (Int32 index = 0; index < sb.Length; index++) 
 if (sb[index] == value) return index; 
 return -1; 
 } 
}

这是可以这样调用sb.IndexOf(‘X’),编译器首先会检查StringBuilder类或它的 基类是否提供了该实例方法。如果存在则编译生成IL代码调用。如果没有匹配的实例方法,则编译器则会查找任何定义了静态的IndexOf方法的静态类,并 且该静态的IndexOf方法接收一个跟发起调用的表达式的类型匹配,该类型必须通过this关键字标记。上面的例子中表达式是sb,类型是 StringBuilder。编译器会查找接收两个参数:StringBuilder(this标记)和Char类型的参数的IndexOf方法。

命名参数和可选参数需要注意的几个部分

1.可以指定方法的参数的默认值,以及作为参数的委托的默认值。

2.具有默认值的参数必须在没有默认值参数的右边。

3.默认值在编译时必须是常量值

4.不能为ref和out类型的参数设置默认值

ref和out区分

1.默认情况下,CLR假定所有方法的参数都是通过值传递

2.从CLR的角度看,ref和out是一样的,生成相同的IL,传递都是对象的地址。元数据除了一位不同(用来区分是out还是ref),其他也一样。

3.C#对待ref和out是不同的方式:
①对于out,调用之前不用初始化,但是ref在调用之前必须初始化

②对于out,在返回之前必须给out参数赋值,而ref参数则不用

params参数数组

当传递很多个同类型的参数时,可以考虑使用参数数组。params关键字告诉编译器对该参数应用一个System.ParamArrayAttribute的实例。


C#编译器检测到调用一个方法时,它会检查所有指定名称的方法,这时是针对不带ParamArray特性的参数方法。如果有匹配的,则调用。如果没有,则
查找具有ParamArray特性参数的方法,检查是否满足。如果能够匹配,则生成构造数组的代码并填充没一个元素,这是在调用选中的方法之前进行的。

只有最后一个参数能够标记为params。调用一个接收参数数组的方法会增加额外的性能消耗,当然如果显示的传递null则不会。所以应该尽可能少的使用参数数组传递。

关于参数类型和返回值类型的选择建议

1.对于方法的参数类型,尽量选择包容性强的类型。例如选择IList类型替代List类型,因为这样除了可以传递IList类型,还可以传递任何实现了该接口IList的类型参数。

2.对于方法的返回值,尽量使用更具体的类型。例如选择FileSteam替代Stream。这样方便我们对调用方法之后,能够在更直接的在小范围处理返回结果。

 

注   《CLR via C#》(Jeffrey Richter著)——.NET 界的经典之作,读的过程写点笔记跟大家分享,我也推荐大家看英文版,能够直接领会原意 

作者:张雪飞
出处:https://zhangxuefei.site/p/96
版权说明:欢迎转载,但必须注明出处,并在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

发表评论

电子邮件地址不会被公开。 必填项已用*标注