运算符和类型强制转换.docx

上传人:b****7 文档编号:9109446 上传时间:2023-02-03 格式:DOCX 页数:50 大小:49.22KB
下载 相关 举报
运算符和类型强制转换.docx_第1页
第1页 / 共50页
运算符和类型强制转换.docx_第2页
第2页 / 共50页
运算符和类型强制转换.docx_第3页
第3页 / 共50页
运算符和类型强制转换.docx_第4页
第4页 / 共50页
运算符和类型强制转换.docx_第5页
第5页 / 共50页
点击查看更多>>
下载资源
资源描述

运算符和类型强制转换.docx

《运算符和类型强制转换.docx》由会员分享,可在线阅读,更多相关《运算符和类型强制转换.docx(50页珍藏版)》请在冰豆网上搜索。

运算符和类型强制转换.docx

运算符和类型强制转换

运算符和类型强制转换

目录

第6章运算符和类型强制转换2

6.1 运算符2

6.1.1 运算符的简化操作3

6.1.2 条件运算符4

6.1.3 checked和unchecked运算符4

6.1.4 is运算符5

6.1.5 as运算符5

6.1.6 sizeof运算符5

6.1.7 typeof运算符6

6.1.8 可空类型和运算符6

6.1.9 空接合运算符6

6.1.10 运算符的优先级6

6.2 类型的安全性7

6.2.1 类型转换7

6.2.2 装箱和拆箱10

6.3 对象的相等比较11

6.3.1 引用类型的相等比较11

6.3.2 值类型的相等比较12

6.4 运算符重载12

6.4.1 运算符的工作方式13

6.4.2 运算符重载的示例:

Vector结构14

6.5 用户定义的数据类型转换19

6.5.1 执行用户定义的类型转换20

6.5.2 多重数据类型转换25

6.6 小结28

前几章介绍了使用C#编写程序所需要的大部分知识。

本章将首先讨论基本语言元素,接着论述C#语言的扩展功能。

本章的主要内容如下:

● C#中的可用运算符

● 处理引用类型和值类型时相等的含义

● 基本数据类型之间的数据转换

● 使用装箱技术把值类型转换为引用类型

● 通过强制转换技术在引用类型之间转换

● 重载标准的运算符,以支持对定制类型的操作

● 给定制类型添加强制转换运算符,以支持无缝的数据类型转换

6.1 运算符

C和C++开发人员应很熟悉大多数C#运算符,这里为新程序员和VisualBasic开发人员介绍最重要的运算符,并介绍C#中的一些新变化。

C#支持表6-1所示的运算符。

表 6-1

类别

运算符

算术运算符

+–*/%

逻辑运算符

&|^~&&||!

字符串连接运算符

+

增量和减量运算符

++––

移位运算符

<<>>

比较运算符

==!

=<><=>=

赋值运算符

=+=–=*=/=%=&=|=^=<<=>>=

成员访问运算符(用于对象和结构)

.

索引运算符(用于数组和索引器)

[]

数据类型转换运算符

()

条件运算符(三元运算符)

?

:

委托连接和删除运算符(见第7章)

+–

对象创建运算符

new

类型信息运算符

sizeof(只用于不安全的代码)istypeofas

溢出异常控制运算符

checkedunchecked

间接寻址运算符

*–>&(只用于不安全代码)[]

命名空间别名限定符(见第2章)

:

:

空接合运算符

?

?

有4个运算符(sizeof、*、->、&)只能用于不安全的代码(这些代码绕过了C#类型安全性的检查),这些不安全的代码见第12章的讨论。

还要注意,sizeof运算符在.NETFramework1.0和1.1中使用,它需要不安全模式。

自从.NETFramework2.0以来,就没有这个运算符了。

类别

运算符

运算符关键字

sizeof(仅用于.NETFramework1.0和1.1)

运算符

*、–>、&

使用C#运算符的一个最大缺点是,与C风格的语言一样,赋值(=)和比较(==)运算使用不同的运算符。

例如,下述语句表示"x等于3":

x=3;

如果要比较x和另一个值,就需要使用两个等号(==):

if(x==3)

{

}

C#非常严格的类型安全规则防止出现常见的C#错误,也就是在逻辑语句中使用赋值运算符代替比较运算符。

在C#中,下述语句会产生一个编译错误:

if(x=3)

{

}

习惯使用宏字符&来连接字符串的VisualBasic程序员必须改变这个习惯。

在C#中,使用加号+连接字符串,而&表示两个不同整数值的按位AND运算。

|则在两个整数之间执行按位OR运算。

VisualBasic程序员可能还没有使用过%(取模)运算符,它返回除运算的余数,例如,如果x等于7,则x%5会返回2。

在C#中很少会用到指针,因此也很少用到间接寻址运算符(->)。

使用它们的唯一场合是在不安全的代码块中,因为只有在此C#才允许使用指针。

指针和不安全的代码见第12章。

6.1.1 运算符的简化操作

表6-2列出了C#中的全部简化赋值运算符。

表 6-2

运算符的简化操作

等价于

x++,++x

x=x+1

x––,––x

x=x–1

x+=y

x=x+y

x–=y

x=x–y

x*=y

x=x*y

x/=y

x=x/y

x%=y

x=x%y

x>>=y

x=x>>y

x<<=y

x=x<

x&=y

x=x&y

x|=y

x=x|y

x^=y

x=x^y

为什么用两个例子来说明++增量和--减量运算符?

把运算符放在表达式的前面称为前置,把运算符放在表达式的后面称为后置。

它们的执行方式有所不同。

增量或减量运算符可以作用于整个表达式,也可以作用于表达式的内部。

当x++和++x单独占一行时,它们的作用是相同的,对应于语句x=x+1。

但当它们用于表达式内部时,把运算符放在前面(++x)会在计算表达式之前递增x,换言之,递增了x后,在表达式中使用新值进行计算。

而把运算符放在后面(x++)会在计算表达式之后递增x--使用x的原值计算表达式。

下面的例子使用++增量运算符说明了它们的区别:

intx=5;

if(++x==6) //true-xisincrementedto6beforetheevaluation

{

Console.WriteLine("Thiswillexecute");

}

if(x++==7) //false-xisincrementedto7aftertheevaluation

{

Console.WriteLine("Thiswon't");

}

第一个if条件得到true,因为在计算表达式之前,x从5递增为6。

第二个if语句中的条件为false,因为在计算完整个表达式(x=6)后,x才递增为7。

前置运算符--x和后置运算符x--与此类似,但它们是递减,而不是递增。

其他简化运算符,如+=和-=需要两个操作数,用于执行算术、逻辑和按位运算,改变第一个操作数的值。

例如,下面两行代码是等价的:

x+=5;

x=x+5;

下面介绍在C#代码中频繁使用的基本运算符和类型转换运算符。

6.1.2 条件运算符

条件运算符(?

:

)也称为三元运算符,是if...else结构的简化形式。

其名称的出处是它带有三个操作数。

它可以计算一个条件,如果条件为真,就返回一个值;如果条件为假,则返回另一个值。

其语法如下:

condition?

true_value:

false_value

其中condition是要计算的Boolean型表达式,true_value是condition为true时返回的值,false_value是condition为false时返回的值。

恰当地使用三元运算符,可以使程序非常简洁。

它特别适合于给被调用的函数提供两个参数中的一个。

使用它可以把Boolean值转换为字符串值true或false。

它也很适合于显示正确的单数形式或复数形式,例如:

intx=1;

strings=x+"";

s+=(x==1?

"man":

"men");

Console.WriteLine(s);

如果x等于1,这段代码就显示1man,如果x等于其他数,就显示其正确的复数形式。

但要注意,如果结果需要用在不同的语言中,就必须编写更复杂的例程,以考虑到不同语言的不同语法。

6.1.3 checked和unchecked运算符

考虑下面的代码:

byteb=255;

b++;

Console.WriteLine(b.ToString());

byte数据类型只能包含0~255的数,所以递增b的值会导致溢出。

CLR如何处理这个溢出取决于许多方面,包括编译器选项,所以只要有未预料到的溢出风险,就需要用某种方式确保得到我们希望的结果。

为此,C#提供了checked和unchecked运算符。

如果把一个代码块标记为checked,CLR就会执行溢出检查,如果发生溢出,就抛出异常。

下面修改代码,使之包含checked运算符:

byteb=255;

checked

{

b++;

}

Console.WriteLine(b.ToString());

运行这段代码,就会得到一个错误信息:

UnhandledException:

System.OverflowException:

Arithmetic

operationresultedinanoverflow.

atWrox.ProCSharp.Basics.OverflowTest.Main(String[]args)

注意:

用/checked编译器选项进行编译,就可以检查程序中所有未标记代码中的溢出。

如果要禁止溢出检查,可以把代码标记为unchecked:

byteb=255;

unchecked

{

b++;

}

Console.WriteLine(b.ToString());

在本例中,不会抛出异常,但会丢失数据--因为byte数据类型不能包含256,溢出的位会被丢掉,所以b变量得到的值是0。

注意,unchecked是默认值。

只有在需要把几个未检查的代码行放在一个明确标记为checked的大代码块中,才需要显式使用unchecked关键字。

6.1.4 is运算符

is运算符可以检查对象是否与特定的类型兼容。

"兼容"表示对象是该类型,或者派生于该类型。

例如,要检查变量是否与object类型兼容,可以使用下面的代码:

inti=10;

if(iisobject)

{

Console.WriteLine("iisanobject");

}

int和其他C#数据类型一样,也从object继承而来;表达式iisobject将得到true,并显示相应的信息。

6.1.5 as运算符

as运算符用于执行引用类型的显式类型转换。

如果要转换的类型与指定的类型兼容,转换就会成功进行;如果类型不兼容,as运算符就会返回值null。

如下面的代码所示,如果object引用不指向string实例,把object引用转换为string就会返回null:

objecto1="SomeString";

objecto2=5;

strings1=o1asstring;     //s1="SomeString"

strings2=o2asstring;    //s1=null

as运算符允许在一步中进行安全的类型转换,不需要先使用is运算符测试类型,再执行转换。

6.1.6 sizeof运算符

使用sizeof运算符可以确定堆栈中值类型需要的长度(单位是字节):

unsafe

{

Console.WriteLine(sizeof(int));

}

其结果是显示数字4,因为int有4个字节。

注意,只能在不安全的代码中使用sizeof运算符。

第12章将详细论述不安全的代码。

6.1.7 typeof运算符

typeof运算符返回一个表示特定类型的System.Type对象。

例如,typeof(string)返回表示System.String类型的Type对象。

在使用反射技术动态查找对象的信息时,这个运算符是很有效的。

第13章将介绍反射。

6.1.8 可空类型和运算符

对于布尔类型,可以给它指定true或false值。

但是,要把该类型的值定义为undefined,该怎么办?

此时使用可空类型可以给应用程序提供一个独特的值。

如果在程序中使用可空类型,就必须考虑null值在与各种运算符一起使用时的影响。

通常可空类型与一元或二元运算符一起使用时,如果其中一个操作数或两个操作数都是null,其结果就是null。

例如:

int?

a=null;

int?

b=a+4;     //b=null

int?

c=a*5;     //c=null

但是在比较可空类型时,只要有一个操作数是null,比较的结果就是false。

即不能因为一个条件是false,就认为该条件的对立面是true,这在使用非可空类型的程序中很常见。

例如:

int?

a=null;

int?

b=-5;

if(a>=b)

Console.WriteLine("a>=b");

else

Console.WriteLine("a

注意:

null值的可能性表示,不能随意合并表达式中的可空类型和非可空类型,详见本章后面的内容。

6.1.9 空接合运算符

空接合运算符(?

?

)提供了一种快捷方式,可以在处理可空类型和引用类型时表示null值。

这个运算符放在两个操作数之间,第一个操作数必须是一个可空类型或引用类型,第二个操作数必须与第一个操作数的类型相同,或者可以隐含地转换为第一个操作数的类型。

空接合运算符的计算如下:

如果第一个操作数不是null,则整个表达式就等于第一个操作数的值。

但如果第一个操作数是null,则整个表达式就等于第二个操作数的值。

例如:

int?

a=null;

intb;

b=a?

?

10;    //bhasthevalue10

a=3;

b=a?

?

10;    //bhasthevalue3

如果第二个操作数不能隐含地转换为第一个操作数的类型,就生成一个编译错误。

6.1.10 运算符的优先级

表6-3显示了C#运算符的优先级。

表顶部的运算符有最高的优先级(即在包含多个运算符的表达式中,最先计算该运算符):

表 6-3

运算符

初级运算符

().[]x++x––newtypeofsizeofcheckedunchecked

一元运算符

+–!

~++x––x和数据类型转换

乘/除运算符

*/%

加/减运算符

+–

移位运算符

<<>>

关系运算符

<><=>=isas

比较运算符

==!

=

按位AND运算符

&

按位XOR运算符

^

按位OR运算符

|

布尔AND运算符

&&

布尔OR运算符

||

条件运算符

?

:

赋值运算符

=+=–=*=/=%=&=|=^=<<=>>=>>>=

注意:

在复杂的表达式中,应避免利用运算符优先级来生成正确的结果。

使用括号指定运算符的执行顺序,可以使代码更整洁,避免出现潜在的冲突。

6.2 类型的安全性

第1章提到中间语言(IL)可以对其代码强制加上强类型安全性。

强类型支持.NET提供的许多服务,包括安全性和语言的交互性。

因为C#这种语言会编译为IL,所以C#也是强类型的。

这说明数据类型并不总是可互换的。

本节将介绍基本类型之间的转换。

注意:

C#还支持在不同引用类型之间的转换,允许指定自己创建的数据类型如何与其他类型进行相互转换。

这些论题将在本章后面讨论。

泛型是C#中的一个特性,它可以避免对一些常见的情形进行类型转换,泛型详见第9章。

6.2.1 类型转换

我们常常需要把数据从一种类型转换为另一种类型。

考虑下面的代码:

bytevalue1=10;

bytevalue2=23;

bytetotal;

total=value1+value2;

Console.WriteLine(total);

在编译这些代码时,会产生一个错误:

Cannotimplicitlyconverttype'int'to'byte'(不能把int类型隐式地转换为byte类型)。

问题是,我们把两个byte型数据加在一起时,应返回int型结果,而不是另一个byte。

这是因为byte包含的数据只能为8位,所以把两个byte型数据加在一起,很容易得到不能存储在byte变量中的值。

如果要把结果存储在一个byte变量中,就必须把它转换回byte。

C#有两种转换方式:

隐式转换方式和显式转换方式。

1.隐式转换方式

只要能保证值不会发生任何变化,类型转换就可以自动进行。

这就是前面代码失败的原因:

试图从int转换为byte,而潜在地丢失了3个字节的数据。

编译器不会告诉我们该怎么做,除非我们明确告诉它这就是我们希望的!

如果在long型变量中存储结果,而不是byte型变量中,就不会有问题了:

bytevalue1=10;

bytevalue2=23;

longtotal;              //thiswillcompilefine

total=value1+value2;

Console.WriteLine(total);

这是因为long类型变量包含的数据字节比byte类型多,所以数据没有丢失的危险。

在这些情况下,编译器会很顺利地转换,我们也不需要显式提出要求。

表6-4介绍了C#支持的隐式类型转换。

表 6-4

源类型

目的类型

sbyte

short、int、long、float、double、decimal

byte

short、ushort、int、uint、long、ulong、float、double、decimal

short

int、long、float、double、decimal

ushort

int、uint、long、ulong、float、double、decimal

int

long、float、double、decimal

uint

long、ulong、float、double、decimal

long、ulong

float、double、decimal

float

double

char

ushort、int、uint、long、ulong、float、double、decimal

注意,只能从较小的整数类型隐式地转换为较大的整数类型,不能从较大的整数类型隐式地转换为较小的整数类型。

也可以在整数和浮点数之间转换,其规则略有不同,可以在相同大小的类型之间转换,例如int/uint转换为float,long/ulong转换为double,也可以从long/ulong转换回float。

这样做可能会丢失4个字节的数据,但这仅表示得到的float值比使用double得到的值精度低,编译器认为这是一种可以接受的错误,而其值的大小是不会受到影响的。

无符号的变量可以转换为有符号的变量,只要无符号的变量值的大小在有符号的变量的范围之内即可。

在隐式转换值类型时,可空类型需要额外考虑:

● 可空类型隐式转换为其他可空类型,应遵循表6-4中非可空类型的转换规则。

即int?

隐式转换为long?

、float?

、double?

和decimal?

● 非可空类型隐式转换为可空类型也遵循表6-4中的转换规则,即int隐式转换为long?

、float?

、double?

和decimal?

● 可空类型不能隐式转换为非可空类型,此时必须进行显式转换,如下一节所述。

这是因为可空类型的值可以是null,但非可空类型不能表示这个值。

2.显式转换方式

有许多场合不能隐式地转换类型,否则编译器会报告错误。

下面是不能进行隐式转换的一些场合:

● int转换为short--会丢失数据

● int转换为uint--会丢失数据

● uint转换为int--会丢失数据

● float转换为int--会丢失小数点后面的所有数据

● 任何数字类型转换为char--会丢失数据

● decimal转换为任何数字类型--因为decimal类型的内部结构不同于整数和浮点数

● int?

转换为int--可空类型的值可以是null

但是,可以使用cast显式执行这些转换。

在把一种类型强制转换为另一种类型时,要迫使编译器进行转换。

类型转换的一般语法如下:

longval=30000;

inti=(int)val;  //Avalidcast.Themaximumintis

这表示,把转换的目标类型名放在要转换的值之前的圆括号中。

对于熟悉C的程序员来说,这是数据类型转换的典型语法。

对于熟悉C++数据类型转换关键字(如static_cast)的程序员来说,这些关键字在C#中不存在,必须使用C风格的旧语法。

这种类型转换是一种比较危险的操作,即使在从long转换为int这样简单的转换过程中,如果原来long的值比int的最大值还大,就会出问题:

longval=;

inti=(int)val;        //Aninvalidcast.Themaximumintis

在本例中,不会报告错误,也得不到期望的结果。

如果运行上面的代码,结果存储在i中,则其值为:

-

最好假定显式数据转换不会给出希望的结果。

如前所述,C#提供了一个checked运算符,使用它可以测试操作是否会产生算术溢出。

使用这个运算符可以检查数据类型转换是否安全,如果不安全,就会让运行库抛出一个溢出异常:

longval=;

inti=checked((int)val);

记住,所有的显式数据类型转换都可能不安全,在应用程序中应包含这样的代码,处理可能失败的数据类型转换。

第14章将使用try和catch语句引入结构化异常处理。

使用数据类型转换可以把大多数数据从一种基本类型转换为另一种基本类型。

例如:

给price加上0.5,再把结果转换为int:

doubleprice=25.30;

intapproximatePrice=(int)(price+0.5);

这么做的代价是把价格四舍五入为最接近的美元数。

但在这个转换过程中,小数点后面的所有数据都会丢失。

因此,如果要使用这个修改过的价格进行更多的计算,最好不要使用这种转换;如果要输出全部计算完或部分计算完的近似值,且不希望用小数点后面的数据去麻烦用户,这种转换是很好的。

下面的例子说明了把一个无符号的整数转换为char型时,会发生的情况:

ushortc=43;

charsymbol=(char)c;

Console.WriteLine(symbol);

结果是ASCII编码为43的字符,即+号。

可以尝试数字类型之间的任何转换(包括char),这种转换是成功的,例如把decimal转换为char,或把char转换为decimal。

值类型之间的转换并不仅限于孤立的变量。

还可以把类型为dou

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 建筑土木

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1