表达式和运算符

表达式


本章将定义表达式,并描述C#提供的运算符。
运算符是一个符号,它表示返回单个结果的操作。操作数(operand)指作为运算符输入的数据元素。一个运算符会:

  • 将操作数作为输入
  • 执行某个操作
  • 基于该操作返回一个值

表达式是运算符和操作数的字符串。可以作为操作数的结构有:

  • 字面量
  • 常量
  • 变量
  • 方法调用
  • 元素访问器,如数组访问器和索引器
  • 其他表达式

例:下面的表达式,有3个运算符和4个操作数


字面量


字面量(literal)是源代码中键入的数字或字符串,表示一个指定类型的明确的、固定的值。

例:字面量

class Program
{
static void Main()
{
Console.WriteLine("{0}",);//整数字面量
Console.WriteLine("{0}",3.1416F);//浮点型字面量
Console.WriteLine("{0}",true);//布尔型字面量
}
}

对于引用类型变量,字面量null表示变量没有设置为内存中的数据。

整数字面量

例:不同的整数类型

  //整型
236L //长整型
236U //无符号整型
236UL//无符号长整型

整数字面量还可以写成十六进制(hex)形式


实数字面量

实数字面量组成如下:

  • 十进制数字
  • 可选的小数点
  • 可选的指数部分
  • 可选的后缀

例:实数字面量的不同格式

float f1=236F;
double d1=236.712;
double d2=.;
double d3=6.338e-26;

无后缀的实数字面量默认是double类型。

字符字面量

字符字面量可以是下面任意一种:

  • 单个字符
  • 简单转义序列:反斜杠+单个字符
  • 十六进制转义序列:反斜杠+大写或小写x+4个十六进制数
  • Unicode转义序列:反斜杠+大写或小写u+4个十六进制数

例:字符字面量的不同格式

char c1='d';
char c2='\n';
char c3='\x0061';
char c4='\u005a';

一些特殊字符及其编码见下图


字符串字面量

两种字符串字面量类型:

  • 常规字符串字面量
  • 逐字字符串字面量

常规字符串字面量包含:

  • 字符
  • 简单转义序列
  • 十六进制和Unicode转义序列
string st1="Hi there!";
string st2="Val\t5,val\t10";
string st3="Add\x000ASome\u0007Interest";

逐字字符串以@为前缀,它有以下特征:

  • 逐字字符串与常规字符串区别在于转义字符串不会被求值。在双引号中间的所有内容,包括通常被认为是转义序列的内容,都被严格按字符串中列出的那样打印
  • 逐字字符串的唯一例外是相邻的双引号组,它们被解释为单个双引号字符
string rst1="Hi there!";
string vst1=@"Hi there!";
string rst2="It started,\"Four score and seven...\"";
string vst2=@"It started,""Four score and seven...""";
string rst3="Value 1 \t 5,val2 \t 10";
string vst3=@"Value 1 \t 5,val2 \t 10";
string rst4="C:\\Program Files\\Microsoft\\";
string vst4=@"C:\Program Files\Microsoft\";
string rst5=" Print \x000A Multiple \u000A Lines";
string vst5=@" Print
Multiple
Lines";

编译器让相同的字符串字面量共享堆中同一内存位置以节约内存

求值顺序


表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式最终值发生差别。

优先级

正如小学的先乘除再加减,C#中运算符也有优先级。


结合性

表达式中运算符优先级不同的,从高到低依次运算。但若是运算符优先级相同怎么办?
当连续运算符有相同优先级时,求值顺序由操作结合性决定。

  • 左结合运算符从左至右
  • 右结合运算符从右至左
  • 除赋值运算符外,其他二元运算符都是左结合
  • 赋值运算符和条件运算符是右结合

简单算术运算符


求余运算符


求余运算符(%)用第二个操作数除第一个操作数,并返回余数。
求余运算符是二元左结合运算符。

  • 0%3=0,因为0除3得0余0
  • 2%3=2,因为2除3的0余2
  • 4%3=1,因为4除3得1余1

关系比较运算符和相等比较运算符


它们都是二元左结合运算符,对其操作数进行比较并返回bool值。

与C和C++不同,在C#中数字不具有布尔意义

int x=;
if(x)//错,x是int类型,不是布尔型
if(x==)//对,返回true

比较操作和相等性操作
对于大多数引用类型来说,比较它们的相等性,将只比较它们的引用。

  • 如果引用相等,即它们指向内存中相同对象,相等性为true,否则为false,即使内存中两个分离的对象在所有其他方面完全相等。
  • 这称为浅比较

下图阐明了引用类型的比较

  • 图左边,a和b两者引用相同,返回true
  • 图右边,引用不同,所以即使内容相同,也返回false

string类型也是引用类型,但它的比较方式不同。比较字符串的相等性,将比较它们的长度和内容(区分大小写)

  • 如果两个字符串长度和内容相等则返回true,即使它们占用不同内存区域
  • 这称为深比较(deep comparison)

将在第15章介绍的委托也是引用类型,并且也使用深比较。比较委托的相等性时,让两个委托都是null,或两者的调用列表有相同数目成员,并且调用列表想匹配,则返回true。
比较数值表达式,将比较类型和值。比较enum类型时,比较操作数的实际值。枚举在第13章阐述。

递增运算符和递减运算符


无论运算符前置还是后置,只影响返回给表达式的值。在语句执行后,最终存放在操作数的变量的值相同


条件逻辑运算符


条件逻辑运算符使用“短路”(short circuit)模式操作,意思是,如果计算Expr1之后结果已确定,那么它会跳过Expr2的求值。
例:短路示例

bool bVal;
bVal=(==)&&(==);
//左侧false,接着&&运算,结果必是false,所以跳过了右侧的运算
bVal=(==)||(==);
//左侧true,接着是||运算,结果必是true,所以跳过了右侧的运算

因为短路特性,不要在Exp2中放置带有副作用的表达式(比如改变一个值),因为可能不会计算。

bool bVal;int iVal=;
bVal=(==)&&(==iVal++);//结果:bVal=False,iVal=10;
↑ ↑
False 不会计算

逻辑运算符


移位运算符


例:移位运算符示例

  • 操作数14的每个位向左移动3个位置
  • 右边结尾腾出位置用0补充
  • 结果为112

int a,b,x=;
a=x<<;
b=x>>;
Console.WriteLine("{0}<<3={1}",x,a);
Console.WriteLine("{0}>>3={1}",x,b);

赋值运算符


赋值运算符是二元右结合运算符

复合赋值
复合赋值运算符允许一种速记方法,在某些情况下避免左边的变量在右边重复出现。
复合赋值不仅更短,也易于理解。

x=x+(y-z);
x+=y-z;

条件运算符


条件运算符是一种强大且简洁的方法,基于条件的结果,返回两个值之一。
条件运算符是三元运算符

  • 格式:Condition?Expression1:Expression2
  • Condition必须返回一个bool类型的值
  • 如果Condition求值为true,那么对Expression1求值并返回。否则,对Expression2求值并返回
if...else
if(x<y)
intVar=;
else
intVar=;
条件运算符
intVar=x<y?:;

if…else语句是控制流语句,它应当用来做两个行为中的一个。条件运算符返回一个表达式,它应当用于返回两个值中的一个。

用户定义的类型转换


用户定义的转换将在第16章详讲,在这里稍微提一下。

  • 可以为自己的类和结构定义隐式和显式转换。这允许把用户定义类型的对象转换成某个其他类型
  • C#提供隐式转换和显示转换
    • 隐式转换,当决定在特定上下文中使用特定类型时,如有必要,编译器会自动执行转换
    • 显式转换,编译器只在使用显式转换运算符时才执行转换

声明隐式转换的语法如下。

       必需的                    目标类型          源数据
↓ ↓ ↓
public static implicit operator TargetType(SourceType Identifier)
{
...
return ObjectOfTargetType;
}

显式转换的语法与之相同,但要用explicit替换implicit

例:将LimitedInt转换为int

class LimitedInt
{
const int MaxValue=;
const int MinValue=;
public static implicit operator int(LimitedInt li)
{
return li.TheValue;
}
public static implicit operator LimitedInt(int x)
{
var li=new LimitedInt();
li.TheValue=x;
return li;
}
private int _theValue=;
public int TheValue
{
get{return _theValue;}
set
{
if(value<MinValue)
_theValue=;
else
_theValue=value>MaxValue?MaxValue:value;
}
}
class Program
{
static void Main()
{
LimitedInt li=;
int value=li;
Console.WriteLine("li:{0},value:{1}",li.TheValue,value);
}
}
}

显式转换和强制转换运算符
如果把两个运算符声明为explicit,你将不得不在实行转换时显示使用转换运算符。

    public static explicit operator int(LimitedInt li)
{
return li.TheValue;
}
public static explicit operator LimitedInt(int x)
{
var li=new LimitedInt();
li.TheValue=x;
return li;
}
static void Main()
{
LimitedInt li=(LimitedInt);
int value=(int)li;
Console.WriteLine("li:{0},value:{1}",li.TheValue,value);
}

输出结果与上例相同

另外有两个运算符,接受一种类型的值,并返回另一种不同的、指定类型的值。这就是is运算符和as运算符。它们将在第16章结尾阐述。

运算符重载


运算符重载允许你定义C#运算符应该如何操作自定义类型的操作数

  • 运算符重载只能用于类和结构
  • 声明必须同时使用static和public修饰符
  • 运算符必须是要操作的类或结构的成员

例:类LimitedInt的两个重载运算符,加运算符和减运算符

class LimitedInt
{
必需的 类型 关键字 运算符 操作数
↓ ↓ ↓ ↓ ↓
public static LimitedInt operator + (LimitedInt x,double y)
{
var li=new LimitedInt();
li.TheValue=x.TheValue+(int)y;
return li;
}
public static LimitedInt operator - (LimitedInt x)
{
var li=new LimitedInt();
li.TheValue=;
return li;
}
...
}
运算符重载的限制

不是所有的运算符都能被重载,可以重载的类型也有限制。

递增和递减运算符可重载。但和预定义的版本不同,重载运算符的前置和后置之间没有区别。
运算符重载不能做下面的事情:

  • 创建新运算符
  • 改变运算符的语法
  • 重新定义运算符如何处理预定义类型
  • 改变运算符的优先级或结合性

重载运算符应该符合运算符的直观含义。

运算符重载的示例

例:LimitedInt的3个运算符重载

class LimitedInt
{
const int MaxValue=;
const int MinValue=;
public static LimitedInt operator - (LimitedInt x)
{
var li=new LimitedInt();
li.TheValue=;
return li;
}
public static LimitedInt operator - (LimitedInt x,LimitedInt y)
{
var li=new LimitedInt();
li.TheValue=x.TheValue-y.TheValue>?(x.TheValue-y.TheValue):;
return li;
}
public static LimitedInt operator + (LimitedInt x,double y)
{
var li=new LimitedInt();
li.TheValue=x.TheValue+(int)y;
return li;
}
private int _theValue=;
public int TheValue
{
get{return _theValue;}
set
{
if(value<MinValue)
_theValue=;
else
_theValue=value>MaxValue?MaxValue:value;
}
}
class Program
{
static void Main()
{
var li1=new LimitedInt();
var li2=new LimitedInt();
var li3=new LimitedInt();
li1.TheValue=;li2.TheValue=;
Console.WriteLine("li:{0},li2:{1}",li1.TheValue,li2.TheValue);
li3=-li1;
Console.WriteLine("-{0}={1}",li1.TheValue,li3.TheValue);
li3=li2-li1;
Console.WriteLine("{0}-{1}={2}",li2.TheValue,li1.TheValue,li3.TheValue);
li3=li1-li2;
Consoel.WriteLine("{0}-{1}={2}",li1.TheValue,li2.TheValue,li3.TheValue);
}
}
}

typeof运算符


typeof运算符返回作为其参数的任何类型的System.Type对象。

例:使用typeof运算符获取SomeClass类的信息

using System.Reflection;//反射
class SomeClass
{
public int Field1;
public int Field2;
public void Method1(){}
public int Method2(){return ;}
}
class Program
{
static void Main()
{
var t=typeof(SomeClass);
FieldInfo[] fi=t.GetFields();
MethodInfo[] mi=t.GetMethods();
foreach(var f in fi)
Console.WriteLine("Field:{0}",f.Name);
foreach(var m in mi)
Console.WriteLine("Method:{0}",m.Name);
}
}

GetType方法也会调用typeof运算符,该方法对每个类型的每个对象都有效。
例:使用GetType获取对象类型名称

class SomeClass
{
}
class Program
{
static void Main()
{
var s=new SomeClass();
Console.WriteLine("Type s:{0}",s.GetType().Name);
}
}

其他运算符


本章介绍的运算符是内置类型的标准运算符。本书后面部分会介绍其他特殊用法的运算符及操作数类型。例如,可空类型有一个特殊运算符叫空接合运算符(第25章)。

C#图解教程 第八章 表达式和运算符的更多相关文章

  1. Python基础0:变量 赋值 表达式和运算符

    变量: 前面我们在使用print()输出内容的时候,如果内容很长,后面要再次输出的时候,就需重新在输入一遍. 如果给输出的内容起个简单的别名.这样我们用简短的别名来代替长内容,下次要输出的时候就直接使 ...

  2. Javascript权威指南——第二章词法结构,第三章类型、值和变量,第四章表达式和运算符,第五章语句

    第二章 词法结构 一.HTML并不区分大小写(尽管XHTML区分大小写),而javascript区分大小写:在HTML中,这些标签和属性名可以使用大写也可以使用小写,而在javascript中必须小写 ...

  3. Javascript学习2 - Javascript中的表达式和运算符

    原文:Javascript学习2 - Javascript中的表达式和运算符 Javascript中的运算符与C/C++中的运算符相似,但有几处不同的地方,相对于C/C++,也增加了几个不同的运算符, ...

  4. JavaScript深入浅出补充——(一)数据类型,表达式和运算符

    项目基本做完,在进行下一阶段学习之前先看视频学习回顾一下JavaScript 一.数据类型 JavaScript中有五种原始类型和一种对象类型 JavaScript弱类型语言中隐式转换 num-0 字 ...

  5. 笔记《JavaScript 权威指南》(第6版) 分条知识点概要3—表达式和运算符

    [表达式和运算符]原始表达式,初始化表达式(对象和数组的),函数定义表达式,属性访问表达式,调用表达式,对象创建表达式,运算符概述,算术表达式,关系表达式,逻辑表达式,赋值表达式,表达式计算,其他运算 ...

  6. Python3基础-表达式和运算符

    表达式和运算符 什么是表达式? 1+2*3就是一个表达式,这里的加号和乘号叫做运算符,1.2.3叫做操作数. 1+2*3经过计算后得到的结果是7,我们可以将计算结果存放在一个变量里,result=1+ ...

  7. Python --表达式和运算符

    表达式 由一个或者几个数字或者变量和运算符组合成的一行代码 通常会返回一个结果 运算符 由一个以上的值经过变化得到新值的过程就叫做运算 用于运算的符号称为运算符 运算符的分类: 算数运算符 比较或者关 ...

  8. C#图解教程读书笔记(第8章 表达式和运算符)

    表达式 字面量 整数字面量 字符字面量 字符串字面量 求值顺序 优先级 结合性 与C和C++不同,在C#中的数字不具有布尔意义. 各种运算符的作用(过) 用户定义类型转换 class XiXiInt ...

  9. 第四章:Javascript表达式和运算符

    表达式是javascript中的一个短语,javascript解释器会将其计算出一个结果.程序中常用量是最简单的一类表达式就是变量.变量名也是一种简单的表达式,它的值就是赋值给变量的值.复杂的表达式是 ...

随机推荐

  1. BZOJ 2738: 矩阵乘法 [整体二分]

    给你一个N*N的矩阵,不用算矩阵乘法,但是每次询问一个子矩形的第K小数. 愚蠢的名字...... 整体二分,影响因子就是矩阵里的数 把$\le mid$的矩阵元素加到二维树状数组里然后询问分成两组就行 ...

  2. 使用 RxJS 实现一个简易的仿 Elm 架构应用

    使用 RxJS 实现一个简易的仿 Elm 架构应用 标签(空格分隔): 前端 什么是 Elm 架构 Elm 架构是一种使用 Elm 语言编写 Web 前端应用的简单架构,在代码模块化.代码重用以及测试 ...

  3. 使用ssh 登录Linux 文件上传下载方法

    最简单的方法: 安装WinSCP或者Filezilla, 启动该程序,然后自己输入输入主机名.端口.用户名.密码登录,然后在putty里面用pwd命令看看当前目录,再在WinSCP/Filezilla ...

  4. 在linux内核中实现自己的系统调用

    如实现一个简单的打印:printk 1.cd linux-ok6410/kernel/ vim printk.cvoid sys_pk(){printk("<0>this is ...

  5. cocos2d-x中处理touch事件

    在cocos2d-x中, touch事件分为两种:一种是单点事件, 另一种是多点事件. 单点事件对应的代理方法是: virtual bool ccTouchBegan(CCTouch *pTouch, ...

  6. 对html语义化的理解

    所有人都知道html即超文本标记语言或超文本链接标示语言,是目前网络上应用最为广泛的语言,也是构成网页文档的主要语言. html标签中的大部分都是由"语义化"标签所担任 那么,它有 ...

  7. MYSQL 基础总结

    学习笔记 [mysql 是不区分大小写的,要区分可以用相应的函数:所有标点符号全是英文状态下的] 一.基础部分 //创建数据库 Create  database  database_name; //使 ...

  8. 8、flask之flask-script组件

    Flask Script扩展提供向Flask插入外部脚本的功能,包括运行一个开发用的服务器,一个定制的Python shell,设置数据库的脚本,cronjobs,及其他运行在web应用之外的命令行任 ...

  9. HDU 3001(状态压缩dp)

    状态压缩dp的第一题! 题意:Mr ACMer想要进行一次旅行,他决定访问n座城市.Mr ACMer 可以从任意城市出发,必须访问所有的城市至少一次,并且任何一个城市访问的次数不能超过2次.n座城市间 ...

  10. Android查缺补漏(IPC篇)-- 款进程通讯之AIDL详解

    本文作者:CodingBlock 文章链接:http://www.cnblogs.com/codingblock/p/8436529.html 进程间通讯篇系列文章目录: Android查缺补漏(IP ...