【读书笔记】C#高级编程 第七章 运算符和类型强制转换

(一)运算符


类别


运算符


算术运算符


+ - * / %


逻辑运算符


& | ^ ~ && || !


字符串连接运算符


+


增量和减量运算符


++ --


移位运算符


<< >>


比较运算符


== != < > <= >=


赋值运算符


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


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


.


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


[]


类型转换运算符


()


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


?:


委托连接和删除运算符


+ -


对象创建运算符


new


类型信息运算符


sizeof is typeof as


溢出异常控制运算符


checked unchecked


间接寻址运算符


[]


名称空间别名限定符


::


空合并运算符


??

1、运算符的简化操作

x+=y等同于x=x+y,赋值运算符中(+= -= *= /= %= &= |= ^= <<= >>=)都是相同的模式。

当x++和++x单独一行时等同于x=x+1。当他们用于较长的表达式内部时,运算符放在前面(++x)会在计算表达式之前增加x。

int x = 0,y=0;

x++;

++y;

if (x==y)

{

    Console.WriteLine("单独一行++在前在后结果一样");

}

if (++x==2)

{

    Console.WriteLine("++在前提前计算");

}

if (y++==2)

{

    Console.WriteLine("++在后没有提前计算");

}

运行以上代码,结果如下:

(1)条件运算符

条件运算符(?:)也称单元运算符,是if...else的简化形式。它判断一个条件真假来返回对应的值(为真返回:之前的值,为假返回:之后的值)。

int x = 0;

string str = x == 0 ? "x等于0" : "x不等于0";

Console.WriteLine(str);

运行以上代码,结果如下:

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

(2)checked运算符和unchecked运算符

C#提供checked运算符和unchecked运算符,来对一个代码块进行标记,是否执行溢出检查。

byte x = 255;

x++;

Console.WriteLine(x);

运行以上代码,结果如下:

这是因为,C#默认使用unchecked运算符不对溢出执行检查,这会导致数据丢失(byte不能超过255,溢出的位会丢失,所以结果为0)。

如果要防止溢出导致的数据丢失,可以使用checked运算符对代码块进行标记。

byte x = 255;

checked

{

    x++;

}

Console.WriteLine(x);

运行以上代码,结果如下:

程序会抛出异常,这个时候我们就能捕捉异常,来防止溢出丢失数据。

(3)is运算符

Is运算符检查对象是否与特定类型兼容(是该类型或者派生自该类型)。

int x = 0;

if (x is object)

{

    Console.WriteLine("x是object类型或派生自object的类型");

}

运行以上代码,结果如下:

(4)as运算符

as运算符用于执行引用类型的显式类型转换。如果要转换的类型与指定的类型兼容,转换就会成功;如果不兼容,as运算符就会返回null。

object obj1 = "字符串";

object obj2 = 0;

string str1 = obj1 as string;

string str2 = obj2 as string;

Console.WriteLine("obj1 as string转换结果:" + str1);

Console.WriteLine("obj2 as string转换结果:" + str2);

运行以上代码,结果如下:

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

(5)sizeof运算符

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

Console.WriteLine(sizeof(int));//4

(6)typeof运算符

typeof运算符返回一个表示特定类型的System.Type对象。例如,typeof(string)返回表示System.String类型的Type类型。在使用反射技术动态地查找对象的信息时,这个运算符很有用。

(7)可空类型和运算符

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

int? x = null;

int? y = x + 1;//null

null值的可能性表示,不能随意合并表达式中的可空类型和非可空类型

(8)空合并运算符

空合并运算符(??)提供了一种快捷方式,可以在处理可空类型和引用类型时表示null可能的值。

例子:

int? x = null;

int y;

y = x ?? 0;

Console.WriteLine(y);

运行以上代码,结果如下:

如果第一个操作数是null,则返回第二个操作数,反之则返回第一个操作数。

2、运算符的优先级

优先级由上到下依次递减



运算符


初级运算符


() . [] x++ x-- new typeof sizeof checked unchecked


一元运算符


+ - ! ~ ++x --x 和数据类型强制转换


乘除运算符


* / %


加减运算符


+ -


移位运算符


<< >>


关系运算符


< > >= <= is as


比较运算符


== !=


按位AND运算符


&


按位XOR运算符


^


按位OR运算符


|


布尔AND运算符


&&


布尔OR运算符


||


条件运算符


?:


赋值运算符


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

建议在复杂表达式中,使用圆括号来显式指定运算符的执行顺序,可以使代码更易于理解。

(二)类型的安全性

1、类型转换

(1)隐式转换

只要保证值不会发生任何变化,类型转换就可以自动(隐式)进行。当值的数量级不受影响,而精度可能受到影响时,也可以进行隐式转换,因为编译器认为这是可以接受的错误。

(2)显式转换

当无法保证值不会发生任何变化时,就需要使用显式转换,否则编译器会报错。

例子:

long类型无法隐式的转换为int,但我们可以显式地进行转换

long lon = 1;

int i = (int)lon;

显式转换是一种比较危险的操作,因为有可能造成溢出,这样导致结果与正确值不同。在进行隐式转换时,应使用checked运算符进行检查。

2、装箱和拆箱

装箱用于描述一个值类型转换为引用类型。

int i = 100;

object obj = i;

拆箱用于描述相反的过程,其中以前装箱的值类型强制转换回值类型。

int i = 100;

object obj = i;

int _i = (int)obj;

(三)比较对象的相等性

对象相等的机制有所不同,这取决于比较的是引用类型(类的实例)还是值类型(基本数据类型、结构或枚举的实例)。

1、比较引用类型的相等性

(1)ReferenceEquals()方法

ReferenceEquals()是一个静态方法,测试两个引用是否引用类的同一个实例,特别是两个引用是否包含内存中的相同地址。最为静态方法,它不可重写。

Person p1, p2;

p1 = new Person();

p2 = new Person();

bool b1 = ReferenceEquals(null, null);

bool b2 = ReferenceEquals(null, p1);

bool b3 = ReferenceEquals(p1, p2);

Console.WriteLine("ReferenceEquals(null, null) 的结果:{0}", b1);

Console.WriteLine("ReferenceEquals(null, p1) 的结果:{0}", b2);

Console.WriteLine("ReferenceEquals(p1, p2) 的结果:{0}", b3);

运行以上代码,结果如下:

(2)虚拟的Equals()方法

Equals()方法是虚拟方法,所以可以在自己的类中重写,从而按值来比较对象。

(3)静态的Equals()方法

Equals()静态版本与其虚拟实例版本的作用相同,其区别是静态版本带有两个参数,并对它们进行相等性比较。

(4)比较运算符==

最好将比较运算符看作严格的值比较和严格的引用比较之间的中间选项。

2、比较值类型的相等性

在比较值类型的相等性时,采用与引用类型相同的规则:ReferenceEquals()用于比较引用,Equals()用于比较值,比较运算符可以看作一个中间选项。但最大的区别是值类型需要进行装箱,才能把它们转换为引用类型,进而才能对它们执行方法。在System.ValueType类中重载了实例方法Equals(),以便对值类型进行合适的相等性测试。

(四)运算符重载

如果要对自定义的类使用运算符,就必须告诉编译器相关的运算符在这个类的上下文中的含义,此时就需要使用运算符重载。

C#要求所有的运算符重载都声明为public和static,这表示它们与它们的类相关联而不是特定实例。重载运算符需要使用operator关键字。编译器处理运算符重载和处理方法重载是一样的。C#要求成对的重载比较运算符,且必须返回布尔值。

例子:

public static decimal operator +(Person lhs, Person rhs)

{

    return lhs.Money + rhs.Money;

}

可以重载的运算符


类别


运算符


限制


算术二元运算符


+、*、/、-、%



算术一元运算符


+、-、++、--



按位二元运算符


&、|、^、<<、>>



按位一元运算符


!、~、true、false


true和false运算符必须成对重载


比较运算符


==、!=、>=、<、<=、>


比较运算符必须成对重载


赋值运算符


+=、-=、*=、/=、>>=、<<=、%=、&=、|=、^=


不能显式地重载这些运算符,在重写单个运算符(如+、-等)时,它们会被隐式地重写


索引运算符


[]


不能直接重载索引运算符。索引器成员类型允许在类和结构上支持索引运算符。


数据类型强制转换运算符


()


不能直接重载类型强制运算符。用户定义的类型强制转换允许定义定制的类型强制转换。

(五)用户定义的类型强制转换

C#允许定义自己的数据类型(结构和类),这意味着需要某些工具支持在自定义的数据类型之间进行类型转换。方法是把类型强制转换运算符定义为相关类的成员运算符,类型强制装换运算符必须标记为隐式或显式,以说明希望如何使用它。我们应遵守与预定义的类型强制转换相同的规则,如果知道无论在元变量中存储什么值,类型强制转换总是安全的,就可以把它定义为隐式强制转换。然而,如果某些数值可能会出错,如丢失数据或抛出异常,就应把数据类型转换定义为显式强制转换。

例子:

显式关键字explicit,隐式关键字implicit

class Program

{

    static void Main(string[] args)

    {

        Water water = new Water(100);

        Ice ice = (Ice)water;//Water显示转换为Ice

        Water water2 = ice;//Ice隐式转换为Water

        Console.ReadKey();

    }

}

public class Water

{

    public int Volume { get; set; }

    public Water(int volume)

    {

        this.Volume = volume;

    }

    public static explicit operator Ice(Water lhs)

    {

        return new Ice(lhs.Volume+1);

    }

}

public class Ice

{

    public int Volume { get; set; }

    public Ice(int volume)

    {

        this.Volume = volume;

    }

    public static implicit operator Water(Ice lhs)

    {

        return new Water(lhs.Volume-1);

    }

}

定义不同结构或类的实例之间的类型强制转换时完全合法的,但有两个限制:

  • 如果某个类派生自另一个类,就不能定义这两个类之间的类型强制转换(这些类型的类型转换已经存在)。
  • 类型强制转换必须在源数据类型或目标数据类型的内部定义。

C#要求把类型强制转换的定义放置在源类(或结构)或目标类(或结构)的内部。它的副作用是不能定义两个类之间的类型强制转换,除非至少可以编辑其中一个类的源代码。这是因为,这样可以防止第三方把类型转换引入类中。

时间: 2024-12-25 03:36:56

【读书笔记】C#高级编程 第七章 运算符和类型强制转换的相关文章

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

SimpleCurrency using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SimpleCurrency { class Program { struct Currency { public uint Dollars; public ushort Cents; public Currency(uint dollars, ushort cents) {

读书笔记 - js高级程序设计 - 第七章 函数表达式

闭包 有权访问另一个函数作用域中的变量的函数 匿名函数 函数没有名字 少用闭包 由于闭包会携带包含它的函数的作用域,因此会比其它函数占用更多的内存.过度使用闭包可能会导致内存占用过多,我们建议读者只在绝对必要时再考虑使用闭包 模块模式   增强的模块模式   特权方法 有权访问私有变量的公有方法叫做特权方法 块级作用域   实现单例的特权方法  

《C#高级编程》【第7章】运算符与类型强制转换 -- 学习笔记

       运算符编程语言的基本元素,它使得我们的代码更加的简洁明了.然而我们为了解决操作数类型不同的问题,我们又有引入了强制转换的概念.我们先看看本章的内容构成吧. 1.运算符 我们来看看一些常见的运算符: <1>条件运算符 其语法如下: Condition ? true_Expression : false_Expression 当条件Condition为真时,其将执行true_Expression,否则执行false_Expression. <2> checked 和

UNIX环境高级编程第七章

这一章主要知识点包括:程序启动与终止,进程终止exit与_axit,atexit函数.存储空间布局,深层嵌套出错跳出函数,资源查询与更改函数等内容. 1.首先从内核传递命令行参数和环境变量值给c启动例程,然后c启动例程调用main函数,main调用其他函数.终止时, 可以使用_exit()函数或者_Exit()函数,直接调用内核结束.也可以调用exit()函数,先进行清理处理.(调用各终止处 理程序,标准I/O清理程序),然后再进入内核. 2.exit函数先调用各终止处理程序,再调用标准I/O流

R in action读书笔记(6)-第七章:基本统计分析(中)

7.2 频数表和列联表 > library(vcd) > head(Arthritis) ID Treatment Sex Age Improved 1 57 Treated Male 27 Some 2 46 Treated Male 29 None 3 77 Treated Male 30 None 4 17 Treated Male 32 Marked 5 36 Treated Male 46 Marked 6 23 Treated Male 58 Marked 7.2.1 生成频数表

R in action读书笔记(6)-第七章:基本统计分析(下)

7.3相关 相关系数可以用来描述定量变量之间的关系.相关系数的符号(±)表明关系的方向(正相关或负相关),其值的大小表示关系的强弱程度(完全不相关时为0,完全相关时为1).除了基础安装以外,我们还将使用psych和ggm包. 7.3.1 相关的类型 1.Pearson.Spearman和Kendall相关 Pearson积差相关系数衡量了两个定量变量之间的线性相关程度.Spearman等级相关系数则衡 量分级定序变量之间的相关程度.Kendall’s Tau相关系数也是一种非参数的等级相关度量.

R in action读书笔记(5)-第七章:基本统计分析

7.1描述性统计分析 > vars<-c("mpg","hp","wt") > head(mtcars[vars])                    mpg  hp    wt Mazda RX4         21.0 110 2.620 Mazda RX4 Wag     21.0 110 2.875 Datsun 710        22.8  93 2.320 Hornet 4 Drive    21.4 11

读书笔记 - js高级程序设计 - 第四章 变量 作用域 和 内存问题

5种基本数据类型 可以直接对值操作 判断引用类型 var result = instanceof Array 执行环境 每个执行环境都有一个与之关联的变量对象,环境中定义的所有变量和函数都保存在这个对象中 执行环境的销毁 某个执行环境中的所有代码执行完毕后 该环境被销毁 保存在其中的所有变量了函数定义也会随之销毁 作用域链中的对象 全局执行环境的变更对象始终都是作用域链中的最后一个对象 没有块级作用域 if 和 for 内的变量 外部也可以访问 标记清除 不同浏览器 只不过垃圾时间的长短不同 引

读书笔记 - js高级程序设计 - 第三章 基本概念 -

启用严格模式 "use strict" 这是一个 pragma 编译指示 让编码意图更清晰  是一个重要原则 5种简单数据类型 Undefined Null Boolean Number String 1种复杂数据类型 Object 检测数据类型的方法 typeof 有如下值: undefined boolean object string number function typeof Null object 意在保存对象还没有保存对象的变量的初始值最好是什么 null 八进制的第一位