1、运算符重载:运算符重重载的关键是在对象上不能总是只调用方法或属性,有时还需要做一些其他工作,例如,对数值进行相加、相乘或逻辑操作等。例如,语句if(a==b)。对于类,这个语句在默认状态下会比较引用 a 和 b 。检测这两个引用是否指向内存中的同一个地址,而不是检测两个实例是否包含相同的数据。然而对于 string 类,这种操作就会重写,于是比较字符串实际上就是比较每个字符串的内容。可以对自己的类进行这样的操作。 对于结构,“==” 运算符在默认状态下是不做任何工作。试图比较两个结构,看看它们是否相等,就会产生一个编译错误。除非显示地重载了“==”,告诉编译器如何进行比较。

  运算符的工作方式

  • int myInteger=3;
    uint myUnsignedInt=2;
    double myDouble=4.0;
    long myLong=myInteger+myUnsignedInt;
    double myOtherDouble=myDouble+myInteger;

    当编译器遇到下面这行代码时会发生什么情况:

  • long myLong=myInteger+myUnsignedInt;

    编译器知道它需要把两个整数加起来,并把结果赋予一个long 型变量。调用一个方法把数字加在一起时,表达式myInteger +myUnsignedInt 是一种非常直观和方便的语法。该方法接受两个参数 myInteger 和myUnsignedInt, 并返回他们的和。所以编译器完成的任务与任何方法调用一样——它会根据参数的类型查找最匹配的“+”运算符重载,这里是带两个整数参数的“+”运算符重载。与一般的重载方法一样,预定义的返回类型不会因为编译器所调用方法的哪个版本而影响编译器的选择。

  • double myOtherDouble=myDouble+myInteger;

    在这个例子中,参数是一个double 类型的数据和一个 int 类型的数据,但“+”运算符没有带这种复合参数的重载形式,所以编译器认为,最匹配的“+”运算符重载是把两个 double 作为其参数的版本,并隐式地把int强制转换为 double。 把两个 double 加在一起与把两个整数加在一起完全不同,浮点数存储为一个尾数和一个指数。把她们加在一起要按位移动一个double的尾数,从而使两个指数有相同的值,然后把尾数加起来,移动所得尾数的数,调整其指数,保证答案有尽可能高的精度。

  • 下面是运算符重载的例子 Vector结构。(在运算符重载时,结构和类的工作方式是一样的。)
  • Vector 是一个三维矢量比如 a(1.0,2.0,3.0) b(-1.0,3.0,-4.0) 相加得到的结果 c=a+b 为(0,5,-1)
  •  1 namespace Com.Test.Yinxi
    2 {
    3 struct Vector
    4 {
    5 public double x,y,z;
    6
    7 public Vector(double x,double y,double z)
    8 {
    9 this.x=x;
    10 this.y=y;
    11 this.z=z;
    12 }
    13
    14 public Vector(Vector rhs)
    15 {
    16 x=rhs.x;
    17 y=rhs.y;
    18 z=rhs.z;
    19 }
    20
    21 public override string ToString()
    22 {
    23 return "("+x+", "+y+", "+z+")";
    24 }

    下面是Vector的运算符提供运算符重载支持

  • public static Vector operator +(Vector lhs,Vector rhs)
    {
    Vector result=new Vector(lhs);
    result.x+=rhs.x;
    result.y+=rhs.y;
    reault.z+=rhs.z; return result;
    }

    c#要求所有的运算符重载都声明为public 和 static ,这表示他们与它们的类或结构相关联,而不是与某个特定实例相关联,所以运算符重载的代码体不能访问非静态成员,也不能访问this标识符;这是可以的,因为参数提供了运算符执行其任务所需要知道的所有输入数据。

   完整代码如下:

  •  

     1 using System;
    2
    3 namespace CSharpTest
    4 {
    5 class OperatorTest
    6 {
    7 public double x, y, z;
    8
    9 public OperatorTest(double x, double y, double z)
    10 {
    11 this.x = x;
    12 this.y = y;
    13 this.z = z;
    14 }
    15
    16 public OperatorTest(OperatorTest oper)
    17 {
    18 x = oper.x;
    19 y = oper.y;
    20 z = oper.z;
    21 }
    22
    23 public override string ToString()
    24 {
    25 return "(" + x + ", " + y + ", " + z+")";
    26 }
    27 public static OperatorTest operator + (OperatorTest lhs, OperatorTest rhs)
    28 {
    29 OperatorTest oper = new OperatorTest(lhs);
    30 oper.x += rhs.x;
    31 oper.y += rhs.y;
    32 oper.z += rhs.z;
    33 return oper;
    34 }
    35
    36 public static void Main(string[] args)
    37 {
    38 OperatorTest oper1 = new OperatorTest(1.0, 2.0, 3.0);
    39 OperatorTest oper2 = new OperatorTest(-1.0, -3.5, -5.0);
    40 //OperatorTest oper3 = new OperatorTest(2.0, 3.0, 1.0);
    41 OperatorTest oper3;
    42 oper3 = oper1 + oper2;
    43 Console.WriteLine(oper1.ToString());
    44 Console.WriteLine(oper2.ToString());
    45 Console.WriteLine(oper3.ToString());
    46
    47 }
    48 }
    49
    50 }

    算术运算符重载的 声明方式

  • //1、
    public static double operator *(operatorTest lhs,operatroeTest rhs)
    {
    //......
    }
    可以根据自己所需要的,所要得到的重载结果 相应的改变 double 函数返回值

   比较运算符的重载

   C#中共有6个比较运算符 它们分为3对

  • == 和!=
  • > 和<
  • >=和<=

 c#要求成对重载比较运算符,即,如果重载了“==”,也就必须重载“!=”;否则会产生编译错误。另外,比较运算符必须返回布尔类型的值。这是它们与算术运算符的根本区别,

    在重载 “==” 和“!=” 时,还必须重载从 System.Object 中继承的 Equals() 和GetHashCode() 方法,否则会产生一个编译警告。原因是Equals()方法应实现与“==”运算符相同类型的相等逻辑。

除了这些区别外,重载比较运算符所遵循的规则与重载算术运算符相同。但比较两个数并不像想象的那么简单,例如,如果只比较两个对象引用,就是比较存储对象的内存地址。比较运算符很少进行这样的比较,所以必须编写代码重载运算符,比较对象的值,并返回相应的布尔结果。

  •  1 public static bool operator ==(OperatorTest lhs,OperatorTest rhs)
    2 {
    3 if(lhs.x==rhs.x&&lhs.y==rhs.y&&lhs.z==rhs.z)
    4 {
    5 return true;
    6 }
    7 else
    8 {
    9 return false;
    10 }
    11 }

    这种方式仅根据矢量元素的值,来对它们进行相等性的比较。对于大多数结构,这就是我们希望的,但在某些情况下,可能需要仔细考虑相等的含义。例如,如果有嵌入的类,那么是应比较引用是否指向同一个对象(浅度比较),还是应比较对象的值是否相等(深度比较)?

  • 浅度比较是比较对象是否指向内存中的同一个位置,而深度比较是比较对象的值和属性是否相等。应根据具体情况进行相等检查,从而有助于确定要验证什么。
  • 不要通过调用从 System.Object 中继承的 Equals()方法的实例版本,来重载比较运算符。如果这么做,在 objA 是 null 时判断 (objA==objB),就会产生一个异常,因为.NET 
    运行库会试图判断null.Equals(objB)。采用其他方法(重写 Equals()方法以调用比较运算符)比较安全
    public static bool operator !=(OperatorTest lhs,OperatorTest rhs)
    {
    return !(lhs==rhs);

可以重载的运算符:

类别 运算符 限制 
算术二元运算符 + 、*、  /、 -、 % 无  
算术一元运算符 +、 -、 ++、 --
按位二元运算符 &、|、^、<<、>>
按位一元运算符 !、~、true、false true 和 false 必须成对重载
比较运算符 ==、!=、>=、<=、<、> 必须成对重载
赋值运算符 += 、-=、 *=、 /=、 >>=、 <<=、 %=、 |=、 ^= 不能显式地重载这些运算符,在重写单个运算符(如+,-,%等)时,它们会被隐式地重写。
索引运算符 [] 不能直接重载索引运算符。索引器成员类型允许在类和结构上支持索引运算符
数据类型强制转换运算符 () 不能直接重载类型强制转换运算符。用户定义的类型强制转换允许定义定制的类型强制转换行为

2、用户定义的类型强制转换

  定义类型强制转换的语法类似于重载运算符,类型强制转换在某种情况下可以看做是一种运算符默,其作用是从源类型转换为目标类型。

public static implicit operator float(Currency value)//implicit 隐式  explicit 显式
{
//....
}

  运算符的返回类型定义了类型强制转换操作的目标类型,它有一个参数,即要转换的源对象。这里定义的类型强制转换可以隐式地把Currency 型的值转换为float 型。 注意,如果数据类型转换声明为隐式,编译器就可以隐式或显式地使用这个转换。如果数据类型转换声明为显式,编译器就只能显式地使用它。与其他运算符重载一样,类型强制转换必须同时声明为public 和static。

3、装箱和拆箱数据类型强制转换

  • 装箱
  • Currency balance =new Currency(,);
    object baseCopy=balance;

    在执行上述隐式地强制转换时,balance的内容被复制到堆上,放在一个装箱的对象上,baseCopy 对象引用被设置为该对象。实际上在后台发生的情况是:在最初定义Currency结构时,.NET Framework 隐式地提供另一个(隐藏的)类,即装箱的Currency 类,它包含于Currency 结构相同的所有字段,但它是一个引用类型,存储在堆上。无论定义的这个值类型是一个结构还是一个枚举,定义它时都存在类似的装箱引用类型,对应于所有的基元值类型,如 int double 和 uint 等。不能也不必再源代码中直接通过编程访问某些装箱类,但在把一个值类型强制转换为object 时,它们实在后台工作的对象。在隐式地把currency转换为object时,会实例化一个装箱的Currency实例,并用Currency结构中的所有数据进行初始化。在上面的代码中,baseCopy对象引用的就是这个已装箱的currency实例。通过这种方式,就可以实现从派生类型到基类型的强制转换,并且,值类型的语法与引用类型的语法一样。

  • 拆箱
  •  object derivedObject=new Currency(,);
    object baseObject=new Object();
    Currency derivedCopy1=(Currency)derivedObject;//可以转换
    Currency derivedCopy2=(Currency)baseObject;//抛出异常

    上述代码的工作方式与前面的引用类型中的代码一样。把derivedObject 强制转换为 Currency 会成功进行,因为derivedObject 实际上引用的是装箱 Currency实例——强制转换的过程是把已装箱的Currency对象的字段复制到一个新的currency 结构中。第二种强制转换会失败,因为 baseObject没有引用已装箱的Currency 对象。

    在使用装箱和拆箱时,这两个过程都把数据复制到新装箱或拆箱的对象上。对装箱对象的操作就不会影响原始值类型的内容。

C#高级编程笔记2016年10月12日 运算符重载的更多相关文章

  1. C#高级编程笔记 2016年10月8日运算符和类型强制转换

    1.checked和unchecked 运算符 C#提供了checked 和uncheckde 运算符.如果把一个代码块标记为checked, CLR就会执行溢出检查,如果发生溢出,就抛出overfl ...

  2. C#高级编程笔记 2016年10月26日 MVC入门 Controller

    1.MVC的定义:   Models: Classes that represent the data of the application  and that use validation logi ...

  3. 2016年10月12日 星期三 --出埃及记 Exodus 18:23

    2016年10月12日 星期三 --出埃及记 Exodus 18:23 If you do this and God so commands, you will be able to stand th ...

  4. 2016年10月12日--string、Math类、Random随机数、DateTime、异常保护

    string string.length; //得到string长度 string.Trim(); //去掉string前后的空格 string.TrimStart(); //去掉string前的空格 ...

  5. 2016年10月31日 星期一 --出埃及记 Exodus 19:16

    2016年10月31日 星期一 --出埃及记 Exodus 19:16 On the morning of the third day there was thunder and lightning, ...

  6. 2016年10月30日 星期日 --出埃及记 Exodus 19:15

    2016年10月30日 星期日 --出埃及记 Exodus 19:15 Then he said to the people, "Prepare yourselves for the thi ...

  7. 2016年10月29日 星期六 --出埃及记 Exodus 19:14

    2016年10月29日 星期六 --出埃及记 Exodus 19:14 After Moses had gone down the mountain to the people, he consecr ...

  8. 2016年10月28日 星期五 --出埃及记 Exodus 19:13

    2016年10月28日 星期五 --出埃及记 Exodus 19:13 He shall surely be stoned or shot with arrows; not a hand is to ...

  9. 2016年10月27日 星期四 --出埃及记 Exodus 19:12

    2016年10月27日 星期四 --出埃及记 Exodus 19:12 Put limits for the people around the mountain and tell them, `Be ...

随机推荐

  1. 解读ASP.NET 5 & MVC6系列(11):Routing路由

    新版Routing功能介绍 在ASP.NET 5和MVC6中,Routing功能被全部重写了,虽然用法有些类似,但和之前的Routing原理完全不太一样了,该Routing框架不仅可以支持MVC和We ...

  2. JAVA的正则表达式-学习

    1.正则表达式作用 正则表达式,是用来匹配字符串的,即检查一定的格式,还能按格式进行分组,替换......其实,不用正则表达式,也可以编.但是,大多数情况下,正则表达式可以提高你编程的效率. 2.学习 ...

  3. ajax同步处理(使得JS按顺序执行)

    在项目中碰到一个问题: 图一: 图二: 函数1代码:这里是因为有ajax请求,默认的是异步的 //点击分页页码,请求后台返回对应页码的数据 function getdata(fewPage,flag, ...

  4. mysql在空闲8小时之后会断开连接(默认情况)

    调试程序的过程发现,在mysql连接空闲一定时间(默认8小时)之后会断开连接,需要重新连接,也引发我对重连机制的思考.

  5. CodeForces - 274B Zero Tree

    http://codeforces.com/problemset/problem/274/B 题目大意: 给定你一颗树,每个点上有权值. 现在你每次取出这颗树的一颗子树(即点集和边集均是原图的子集的连 ...

  6. 定制sqlmap tamper脚本

    前言 渗透测试过程中遇到注入点常常丢到sqlmap中进行测试,假如网站有waf,sqlmap便无法直接注入了. 测试 在测试某个项目的过程中,一个页面的aid参数,习惯性的提交 and 1=1发现直接 ...

  7. zlib-1.2.7/libpng-1.5.9 instead of zlib-1.2.8/libpng-1.6.6

    The reason for the failure apparently appears to be version incompatibility, partly may be due to li ...

  8. 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(四)

    SpringSecurity(1) 其实啊,这部分我是最不想写的,因为最麻烦的也是这部分,真的是非常非常的麻烦.关于SpringSecurity的配置,让我折腾了好半天,网上的配置方式一大把,但总有一 ...

  9. MSSQLServer 纵向表转横向表 横向表转纵向表 行转列 列转行

    MSSQLServer 纵向表转横向表  横向表转纵向表 建表语句及插入数据语句: CREATE TABLE Test_y( ) NULL, ) NULL, [Grade] [int] NULL ) ...

  10. 测试开发面试-java持续累积

    接口和抽象类的区别 对java线程的理解 对java并发的理解 webservice的特点,用webservice的原因 守护线程和非守护线程 单例的实现,单例并发 如何实现定义一个类,只实现接口的任 ...