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

运算符重载方法

一 些编程语言允许定义运算符如果操作类型的实例,例如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方法名:

View Code

运算符重载是非常有用的工具,可以让开发者用简洁的代码来表达思想。然而,并不是所有的编程语言都支持运算符重载。当使用不支持运算符重载的语言时,编译器会报错。此时应该允许直接调用如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<T>类型替代List<T>类型,因为这样除了可以传递IList<T>类型,还可以传递任何实现了该接口IList<T>的类型参数。

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

 

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

时间: 2024-10-23 10:41:55

运算符重载方法,扩展方法,以及方法参数的相关文章

c++-C++ list赋值和类的运算符重载

问题描述 C++ list赋值和类的运算符重载 刚刚发现了个问题,一直卡着我 class CA { public: CA(){} ~CA(){} public: VOID operator = ( CA& msg ) { dwvalue = msg.dwvalue; } private: DWORD dwvalue; }; VOID Fuck1( OUT list& bb ) { list< CA > aa; CA a; CA b; aa.push_back(a); aa.pu

C++之运算符重载(1)

在前一节中曾提到过,C++中运行时的多态性主要是通过虚函数来实现的,而编译时的多态性是由函数重载和运算符重载来实现的.这一系列我将主要讲解C++中有关运算符重载方面的内容.在每一个系列讲解之前,都会有它的一些基础知识需要我们去理解.而运算符重载的基础就是运算符重载函数.所以今天主要讲的是运算符重载函数. 1.运算符重载是对已有的运算符赋予多重含义,使同一个运算符作用域不同类型的数据导致不同行为的发生.比如 int i; int i1=10,i2=10; i=i1+i2; std::cout<<

C++运算符重载的方法详细解析_C 语言

运算符重载实质上是函数的重载 重载运算符的函数一般格式如下: 函数类型    operator  运算符名称    (形参表列) {对运算符的重载处理} 例如,想将"+"用于Complex(复数)的加法运算,函数的原型可以是这样的: 复制代码 代码如下: Complex operator + (Complex & c1,Complex &c2); 其中,operator是关键字,时候专门用于定义重载运算符的函数的,运算符名称就是C++提供给用户的预定运算符. 注意:函数

php从给定url获取文件扩展名的方法

 本文实例讲述了php从给定url获取文件扩展名的方法.分享给大家供大家参考.具体实现方法如下: <?php /** * 给定url,获取文件后缀 * @param string $url * @return string */ function getUrlPostfix ($url) { $url_arr = explode('.', $url); $postfix = $url_arr[count($url_arr) - 1]; $substr = substr($postfix, 0,

[C#]Attribute特性(2)——方法的特性及特性参数

 上篇博文[C#]Attribute特性介绍了特性的定义,类的特性,字段的特性,这篇博文将介绍方法的特性及特性参数相关概念. 3.方法的特性        之所以将这部分单列出来进行讨论,是因为对方法的特性查询的反射代码不同于对类的特性查询的反射代码.在这个例子里,我们将使用一个特性用来定义一种可进行事务处理的方法.    1 public class TransactionableAttribute : Attribute 2 { 3 public TransactionableAttribu

java根据方法名称取得反射方法的参数类型示例_java

复制代码 代码如下: /** * 根据方法名称取得反射方法的参数类型(没有考虑同名重载方法使用时注意) * @param obj         类实例   * @param methodName  方法名 * @return * @throws ClassNotFoundException */public static Class[]  getMethodParamTypes(Object classInstance,  String methodName) throws ClassNotF

修改或扩展jQuery原生方法的代码实例_jquery

修改或者扩展jQuery的方法代码实例: 毫无疑问,jQuery是一款功能强大且使用方便的类库. 从它的广泛应用可以证实上面的观点,但是正所谓人无完人,金无足赤,jQuery也是如此,并非在任何时候或者场合都能够完美的完成我们的任务,所以有事以后就需要对jQuery原有的方法进行扩展修改,但是最好方法仍然具有原来的功能. 代码实例: 复制代码 代码如下: <!DOCTYPE html> <html> <head> <meta charset=" utf-

探索Clojure将协议用作扩展机制的方法

"没有继承性的扩展,第 1 部分" 主要讨论了 Goovy.http://www.aliyun.com/zixun/aggregation/16945.html">Scala 和 Clojure 中为现有类添加新方法的机制,这也是 Java 下一代语言实现无继承扩展的方法之一.本文将探讨 Clojure 的协议如何以创新的方法拓展 Java 扩展功能,为表达式问题提供出色的解决方案. 尽管这期文章主要关注可扩展性,但也会略为涉及一些允许 Clojure 和 Java 代

python进行C扩展的各种方法本质原理是什么?

问题描述 python进行C扩展的各种方法本质原理是什么? 我学习了利用C API,Ctypes,Cython等C扩展方式的使用方法,我想这些方法在底层应该有共通的实现原理,我想请教一下高手解释一下这个本质原理,对于C扩展模块能够引入Python与Python模块相互调用的本质原因是什么? 解决方案 Python是用C语言实现的一种脚本语言,本身具有优良的开放性和可扩展性,并提供了方便灵活的应用程序接口(API),从而使得C/C++程序员能够在各个级别上对Python解释器的功能进行扩展.在使用