配色: 字号:
运行符和强制类型转换
2017-12-16 | 阅:  转:  |  分享 
  
运算符和强制类型转换要点:1.C#中的运算符2.处理引用类开和值类型时相等的含义3.基本数据类型之间的数据转换4.使用用装箱技术把使用类型转
换为引用类型5.通过类型强制转换在引用类型之间转换6.重载标准的运行符以支持自定义类型7.给自定义类型添加类型强制转换运算本章将首
先讨论基本语言元素,接着论述C#语言的拓展功能。运行算符C和C++开发人员应很熟悉大多数C#运算符,这里为新程序员介绍最重的运行符
,并揭示C#中的一些新变化。类别运算符算术运算符+-/%逻辑运算符&|^~&&||!字符串连接运算符
+增量和减量运算符++--移位运算符<<>>比较运行符==!=<><=>=赋值运算符=+=-=
=/=%=&=|=^=<<=>>=成员访问运算符.索引运行符[]类型转换运算符()条件运算符?:委托连
接和删除运算符+-对象创建运算符new类型信息运算符sizeofistypeofas溢出异常控制运算符checked
unchecked间接寻址运算符[]名称空间运算符::空合并运算符??如下表示:类别运算符关键字Sizeof(已废弃不用)运
行符,->,&(用于不安全的代码)checked和unchecked运算符byteb=255;checked
{b++;}Console.WriteLine(b);此处会抛出OverfloatException异常。byteb=
255;unchecked{b++;}Console.WriteLine(b);没有异常,输出0,unchecked是默
认行为。is运算符检测对象是否与特定类型兼容;istaticvoidMain(string[]args){inta
=0;Console.WriteLine(aisobject);//TrueDeviced=newDevice
();Console.WriteLine(disBase);//TrueConsole.WriteLine(disb
yte);//False}classBase{}classDevice:Base{}as运算符用于执行引
用类型的显示转换。如果类型不兼容则返回nullobjecta=0;objects="1236";strings
1=sasstring;//s1="1236"strings2=aasstring;//s2=nu
lltypoef返回一个表示特定类的Type对象。可空类型和运行符int?a=null;int?b=6+a;
//nullint?c=5+a;//null只要有一个操作数是null,比较结果就是false.空合并运算符in
t?a=null;intb;b=a??10;//等价于b=a==null?10:a;类型的安全性类
型转换bytea=10;bytec=30;byted=a+c;编译时出现错误!!两个byte相加返回一
个int类型。C#支持两种转换方式:隐式转换只要保证值不会发生改变,类型轩换就可以自动进行。2显示转换longval=300
0;inti=(int)val;//强制类型转换此方法有数据溢出危险,不建议使用。比较类型的相等性ReferenceEqua
ls()测试两个引用是否引用类的同一个实例,特别是两个引用是否包含内存中的相同地址。虚拟的Equals()Object中比较的是引
用,但因为是虚拟的方法所以可以重写它从面按值来比较对象。特别的,如果希望类的实例用作字典的键,就需要重写这个方法,以比较相关值。否
则,根据重写object.GetHashCode()方式,包含对象的字典类要么不工作,要么工作效率非常低。静态的Equals()传
入两个object对象进行比较,对有一个是null或两个都是nul的情况作处理,如果两个都不同null就调用obejct的Equa
ls方法,所以重写的虚拟的Equals方法后就相当于重写的静态的Equals方法。比较运算符(==)大多数情况下x==y比较
的是引用。但是,如果把一些类看作值,其含义会比较直观,这是要可以接受的。这种情况下最好重写比较运算符,以执行值的比较。比较值类型的
相等性在比较值类型的相等性时,采用与引用类型相同的规则:RefrenceEquals—>引用,Equals值,比较运算符可以看是一
个中间选项。但是最大的区别是值类型需要装箱操作,才能把它们转换为引用,进面才能对它们执行方法。另外,System.ValueTyp
e类中重载了实例方法Equals,以便对类型进行合适的相等性测试。如果调用sA.Equals(sB),其中sA,sBj是某个结构的
实例,则根据sA,sB是否在其所有的字段中包含相同的值,而返回true或false.另一方面,不能对结构体使用==运算符,除非
在代码中为这个结构体重载了==运算符另外RefrenceEquals()在应用于值类型时,它总是返回false,因为调用这个方法,
值类型需要装箱到对象中。重载运行符运算符的工作方式当编译器遇到这行代码时会发生什么情况?Mylong=myInt1+myU
nsigned;编译器知道,它需要把两个整数加起来,并把结果赋予一个long型变量。调用一个方法把数字加在起时,Mylong=
myInt1+myUnsigned是一种非常直观的语法。这个方法接收两个参数myInt1,myUnsigned并返回它们的和。
所以编译器完成的任务与调用任何方法一样,它需要根据参数类型查找最匹配的“+”重载,这里是带两个参数的“+”重载。与一般和重载方
法一样,返回类型不会影响编译器的匹配策略。运算符重载示例usingSystem;usingSystem.Collections
.Generic;usingSystem.Linq;usingSystem.Text;namespace重载运算符{str
uctVector{publicintx,y,z;publicVector(intx,inty,int
z){this.x=x;this.y=y;this.z=z;}publicVector(Vectorr
hs){this.x=rhs.x;this.y=rhs.y;this.z=rhs.z;}publicov
erridestringToString(){return"("+x+","+y+","+z+
")";}publicstaticVectoroperator+(Vectorlhs,Vectorrhs){
Vectorresult=newVector(lhs);result.x+=rhs.x;result.y+=r
hs.y;result.z+=rhs.z;returnresult;}}}添加更多重载首先要重载乘法运算符,以支持标
量和矢量相乘以及矢量和矢量相乘.publicstaticVectoroperator(doublelhs,Vec
torrhs){returnnewVector(rhs.xlhs,rhs.ylhs,rhs.zlhs)
;}//上一个重载中,Vectordouble参数不匹配publicstaticVectoroperator
(Vectorlhs,doublerhs){returnrhslhs;}下一步重载矢量的点乘内积操作publ
icstaticdoubleoperator(Vectorlhs,Vectorrhs){returnlhs
.xrhs.x+lhs.yrhs.y+lhs.zrhs.z;}测试代码如下:Vectorv=ne
wVector(1,2,3);v=2;Console.WriteLine(v);Vectorv1=newVe
ctor(1,1,1);v1+=v;Console.WriteLine(v1);运行结果正确,但是我们并没重载(+=或者
=)运算符,为什么也能得到正确的结果?虽然“+=”一般作为单个运算符,但实际上它对应的操作分为两步:相加赋值与C++不同,C
#不许重载”=”运算符,但如果重载’+’运算符,编译器会自动使用“+”的重载来执行“+=“的操作。-==等也遵循此规则
。比较运行算符的重载分为3对:==和!=>和<>=和<=C#要求成对重载比较运算符.否则会产生编译错误.另外比较运行符返回的是bo
ol值。在重载==和!=时,还必须重写Equals()和GetHashCode,否则编译会产生一个警告。不要通过调用ob
ject.Equals()来重载比较运行符。如果这么做,在objA=null时(objA==objB)就会产生一个异常,
因为.net运行库会试图判断null.Equals(objB).publicstaticbooloperator==(V
ectorlhs,Vectorrhs){returnlhs.x==rhs.x&&lhs.y==rhs.y&
&lhs.z==rhs.z;}publicstaticbooloperator!=(Vectorlhs,Ve
ctorrhs){return!(lhs==rhs);}用户定义的类型强制转换inti=3;longI
=i;//隐式转换shorts=(short)i;//显示转换实现用记定义的类型强制转换usingSystem
;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.
Text;namespace用户定义的类型强制类型转换{classCurrency{uintdollars;UInt
16cents;publicuintDollars{get{returndollars;}set{dol
lars=value;}}publicushortCents{get{returncents;}set
{cents=value;}}publicCurrency(uintdollars,ushortcents)
{this.Dollars=dollars;this.Cents=cents;}publicoverride
stringToString(){returnstring.Format("${0}.{1,-2:00}",Dollars
,Cents);}}}现在有如下代码:Currencybalance=newCurrency(10,50);flo
atf=balance;//我们希望得到10.5为此我们需要定义一种类型强制转换。//这种转换没有风险,应实现成隐式转换
staticpublicimplicitoperatorfloat(Currencyvalue){returnv
alue.Dollars+value.Cents/100.0f;}将float转成Currencystaticpub
licexplicitoperatorCurrency(floatvalue){checked{uintdoll
ars=(uint)value;UInt16cents=Convert.ToUInt16((value-dollars
)100);returnnewCurrency(dollars,cents);}}类之间的类型强制转换定义不同结构或类的实例之间的类型强制转换是完全合法的,但有两个限制:1.如果某个类派生自另一个类,就不能定义这两个类之间的类型强制转换(这些类型的类型转换已经存在)2.类型强制转换必须在源数据或目标数据类型内部字义如上图,唯一合法的自定义类型强制转换就是类C和类D之间的转换,因为这些类并没有互相派生。对于这些类型强制转换,可以选择放置在C或者D类内部,但不能在其它地方定义。一旦在一个类的内部定义了类型强制类型转换,就不能在另一个类中定义相同的类型强制转换。显然,对于每一种转换只能有一种类型强制转换,否则编译就不知道该选哪个类型强制转换了。
献花(0)
+1
(本文系luan_it首藏)