第9章 表达式和运算符 笔记
第9章 表达式和运算符 笔记
9.1 表达式
操作符是输入的数据元素,可以作为操作数的结构有:字面量、常量、变量、方法调用、元素访问器、其他表达式。
运算符是一个符号,将操作数作为输入,执行某个操作,返回该操作的结果。
运算符有一个到3个操作数。
表达式是运算符和操作数的字符串,可以使用运算符组合表达式创建更复杂的表达式。
表达式求值是将每个运算符,以适当的顺序应用到它的操作数,产生一个值的过程。
9.2 字面量
字面量是代码中的数字或字符串,表示一个指定类型的、明确的、固定的值。
字面量的值必须在编译时可知。
bool型有两个字面量:true、false
引用类型字面量 null
9.2.1 整数字面量
整数字面量是最常用的字面量,常被书写为十进制数字形式,没有小数点,带有可选的后最,指明整数的类型。
十六进制整数:以 0x 或 0X 开始,数字从0到F。
二进制整数:以 0b 或 0B 开始,数字必须是0或1。
十六进制和二进制记法用前缀指定,实际的数据类型用后缀指定。
整数字面量的后缀
| 后缀 | 整数类型 | 备注 |
|---|---|---|
| 无 | int、uint、long、ulong | |
| U、u | uint、ulong | |
| L、l | long、ulong | 不推荐使用小写字母l,因为它很容易和数字1弄混 |
| ul、uL、Ul、UL、lu、Lu、lU、LU | ulong | 不推荐使用小写字母l,因为它很容易和数字1弄混 |
可以在数字字面量中插入分隔符以看清数字大小:
Console.WriteLine("5_000_000_000 is much easier to read than 5000000000");
9.2.2 实数字面量
三种实数数据类型:float、double、decimal,分别对应32位、64位、128位精度。
其内部由两部分组成,实际的数字、表示小数点位置的指数。
double是最常用的实数数据类型。
实数字面量的组成:
十进制数字
一个可选的小数点
一个可选的指数部分
一个可选的后缀
float f1 = 236F;
double d1 = 236.714;
double d2 = .35192;
double d3 = 6.338e-26;
实数字面量的后缀,无后缀的实数字面量是double类型
| 后缀 | 实数类型 |
|---|---|
| 无 | double |
| F、f | float |
| D、d | double |
| M、m | decimal |
9.2.3 字符字面量
字符字面量由两个单引号内 的字符组成,用于表示单个字符、非打印字符、执行特殊任务的字符。
字符字面量包括:
- 单个字符:char。
- 简单转义序列:反斜杠 + 单个字符。
- 十六进制转义序列:反斜杠 + x/X + 4 位十六进制数字。
- Unicode 转义序列:反斜杠 + u/U + 4 位十六进制数字。
char c1 = 'd'; // 单个字符
char c2 = '\n'; // 简单转义序列
char c3 = '\x0061'; // 十六进制转义序列
char c4 = '\u005a'; // Unicode 转义序列
特殊字符
| 名称 | 转义序列 | 十六进制编码 |
|---|---|---|
| 空字符 | \0 | 0x0000 |
| 警告 | \a | 0x0007 |
| 退格符 | \b | 0x0008 |
| 水平制表符 | \t | 0x0009 |
| 换行符 | \n | 0x000A |
| 垂直制表符 | \v | 0x000B |
| 换页符 | \f | 0x000C |
| 回车符 | \r | 0x000D |
| 双引号 | " | 0x0022 |
| 单引号 | ’ | 0x0027 |
| 反斜杠 | \ | 0x005C |
9.2.4 字符串字面量
字符串字面量使用双引号标记,
字符串字面量类型包括两种:
-
常规字符串字面量。
可以包含上述 4 种字符字面量。
-
逐字字符串字面量。
- 以 @ 字符为前缀。
- 字符串中的转义序列不会被求值。
- 相邻的双引号组,被解释为单个双引号字符
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"; // Interprets tab esc sequence
string vst3 = @"Value 1 \t 5, Val2 \t 10"; // Does not interpret tab
string rst4 = "C:\\Program Files\\Microsoft\\";
string vst4 = @"C:\Program Files\Microsoft\";
string rst5 = " Print \x000A Multiple \u000A Lines";
string vst5 = @" Print
Multiple
Lines";
// output
Hi there!
Hi there!
It started, "Four score and seven..."
It started, "Four score and seven..."
Value 1 5, Val2 10
Value 1 \t 5, Val2 \t 10
C:\Program Files\Microsoft\
C:\Program Files\Microsoft\
Print
Multiple
Lines
Print
Multiple
Lines
编译器让相同的字符串字面量共享堆中的同一内存位置以节约内存。
9.3 求值顺序
表达式可以由多个嵌套的子表达式组成,子表达式的求值顺序可以使表达式的最终值发生变化。
9.3.1 优先级
C#超过45个运算符和14个优先级
运算符优先级:从高到低
| 分类 | 运算符 |
|---|---|
| 初级运算符 | a.x、f(x)、a[x]、x++、x–、new、typeof、checked、unchecked |
| 一元运算符 | +、-、!、~、++X、–X、(T)X |
| 乘法 | *、/、% |
| 加法 | +、- |
| 移位 | <<、>> |
| 关系和类型 | <、>、<=、>=、is、as |
| 相等 | ==、!= |
| 位与 | & |
| 位异或 | ^ |
| 位或 | | |
| 条件与 | && |
| 条件或 | || |
| 条件选择 | ?: |
| 赋值运算符 | =、*=、/=、%=、+=、-=、<<=、>>=、&=、^=、|= |
9.3.2 结合性
运算符的优先级不同,编译器根据优先级从高到低计算。
左结合运算符:从左至右求值
右结合运算符:从右至左求值
赋值运算符、条件运算符是右结合的,其他二元运算符是左结合的。
运算符结合性总结
| 运算符类型 | 结合性 |
|---|---|
| 赋值运算符 | 右结合 |
| 其他二元运算符 | 左结合 |
| 条件运算符 | 右结合 |
9.4 简单算术运算符
基本四则运算符:加减乘除,左结合运算符
9.5 求余运算符
求余运算符返回余数
9.6 关系比较运算符和相等比较运算符
比较操作数,返回bool值
比较操作和相等性操作
-
**浅比较:**对于大多数引用类型来说,比较相等性时,只比较它们的引用。
- 如果指向相同的引用对象,则为 true;否则为 false。
- 因此,若两个不同的引用指向的对象值内容相同,也返回 false。
-
**深比较:**string 类型对象不使用浅比较,而是比较字符值是否相等。
- 如果两个字符串有相同的长度和内容(区分大小写),则为 true;否则为 false;
- 即使两个字符串占用不同的内存区域,如果值相同,也会返回 true。
委托也是引用类型,也使用深比较。
9.7 递增运算符和递减运算符
递增运算符对操作数加1,递减运算符对操作数减1。
两种形式:
前置:放在操作数之前,处理操作数,返回新值
后置:放在操作数之前,处理操作数,返回旧值
9.8 条件逻辑运算符
逻辑运算符用于比较或否定它们操作数的逻辑值,返回结果。
逻辑与 && 、逻辑或 || 是二元左结合运算符
逻辑非 ! 是一元运算符
| 运算符 | 名称 | 描述 |
|---|---|---|
| && | 与 | 如果两个操作数都是 true,结果为 true;否则为 false |
| || | 或 | 如果至少一个操作数是 true,结果为 true;否则为 false |
| ! | 非 | 如果操作数是 false,结果为 true;否则为 false |
条件逻辑运算符使用短路模式,前面结果确定后,会跳过后面的结果
bool bVal;
int iVal = 10;
// 前面 1 == 2是false,后面不会执行
bVal = (1 == 2) && (9 == iVal++);
// 结果 bVal = False, iVal = 10
9.9 按位逻辑运算符
按位逻辑运算符常用于设置位组的方法参数。
按位非是一元运算符。
| 运算符 | 名称 | 描述 |
|---|---|---|
| & | 位与 | 产生两个操作数的按位与。仅当两个操作位都为1时结果位才是1 |
| | | 位或 | 产生两个操作数的按位或。只要任意一个操作位为1时结果位就是1 |
| ^ | 位异或 | 产生两个操作数的按位异或。仅当一个而不是两个操作数为1时结果位为1 |
| ~ | 位非 | 操作数的每个位都取反。该操作得到操作数的二进制反码(数字的反码是其二进制形式按位取反的结果。也就是说,每个0都变成1,每个1都变成0) |
9.10 移位运算符
按位运算符将位组向左或向右移动指定数目个位置,空位用0或1填充。
移位运算符是二元左结合运算符,语法格式为 Operand << Count(左移)或 Operand >> Count(右移),其中 Count 指定移动的位置数。
| 运算符 | 名称 | 描述 |
|---|---|---|
| << | 左移 | 将位组向左移动给定数目个位置。位从左边移出并丢失。右边打开的位位置用0填充 |
| >> | 右移 | 将位组向右移动给定数目个位置。位从右边移出并丢失 |
9.11 赋值运算符
9.12 条件运算符
条件运算符是三元运算符,基于条件的结果,返回两个值之一。
intVar = x < y ? 5 : 10;
9.13 一元算术运算符
一元算法运算符 设置数字值的符合
9.14 用户定义的类型转换
可以位自己的类和结构,定义隐式转换和显示转换。
隐式转换
使用 public 和 static 修饰符,implicit 表明为隐式转换:
public static implicit operator TargetType ( SourceType Identifier )
{
// ...
return ObjectOfTargetType;
}
class LimitedInt
{
const int MaxValue = 100;
const int MinValue = 0;
public static implicit operator int(LimitedInt li) // Convert type
{
return li.TheValue;
}
public static implicit operator LimitedInt(int x) // Convert type
{
LimitedInt li = new LimitedInt();
li.TheValue = x;
return li;
}
private int mTheValue = 0;
public int TheValue { // Property
get { return mTheValue; }
set
{
if (value < MinValue)
mTheValue = 0;
else
mTheValue = value > MaxValue
? MaxValue
: value;
}
}
}
class Program {
static void Main() // Main
{
LimitedInt li = 500; // Convert 500 to LimitedInt
int value = li; // Convert LimitedInt to int
Console.WriteLine($"li: { li.TheValue }, value: { value }");
}
}
// output
li: 100, value: 100
显示转换
和隐式转换一样,但使用 explicit 而不是 implicit:
public static explicit operator int(LimitedInt li)
{
return li.TheValue;
}
class LimitedInt
{
const int MaxValue = 100;
const int MinValue = 0;
public static explicit operator int(LimitedInt li) // Convert type
{
return li.TheValue;
}
public static explicit operator LimitedInt(int x) // Convert type
{
LimitedInt li = new LimitedInt();
li.TheValue = x;
return li;
}
private int mTheValue = 0;
public int TheValue { // Property
get { return mTheValue; }
set
{
if (value < MinValue)
mTheValue = 0;
else
mTheValue = value > MaxValue
? MaxValue
: value;
}
}
}
class Program {
static void Main() // Main
{
LimitedInt li = (LimitedInt)500; // Convert 500 to LimitedInt
int value = (int)li; // Convert LimitedInt to int
Console.WriteLine($"li: { li.TheValue }, value: { value }");
}
}
// output
li: 100, value: 100
9.15 运算符重载
C#操作自定义类型的操作数
- 运算符重载只能用于类和结构。
- 使用
operator x重载运算符 x。- 一元运算符重载方法只带一个单独的 class 或 struct 参数。
- 二元运算符重载方法带两个参数,至少有一个需要为 class 或 struct。
public static LimitedInt operator -(LimitedInt x) // Unary
public static LimitedInt operator +(LimitedInt x, double y) // Binary
运算符重载的说明:
- 必须同时使用 public 和 static。
- 运算符方法必须是要操作的类或结构的成员。
class LimitedInt Return
{
// Required Type Keyword Operator Operand
public static LimitedInt operator + (LimitedInt x, double y)
{
LimitedInt li = new LimitedInt();
li.TheValue = x.TheValue + (int)y;
return li;
}
public static LimitedInt operator - (LimitedInt x)
{
// In this strange class, negating a value just sets it to 0.
LimitedInt li = new LimitedInt();
li.TheValue = 0;
return li;
}
// ...
}
9.15.1 运算符重载的示例
class LimitedInt
{
const int MaxValue = 100;
const int MinValue = 0;
public static LimitedInt operator -(LimitedInt x)
{
// In this strange class, negating a value just sets its value to 0.
LimitedInt li = new LimitedInt();
li.TheValue = 0;
return li;
}
public static LimitedInt operator -(LimitedInt x, LimitedInt y)
{
LimitedInt li = new LimitedInt();
li.TheValue = x.TheValue - y.TheValue;
return li;
}
public static LimitedInt operator +(LimitedInt x, double y)
{
LimitedInt li = new LimitedInt();
li.TheValue = x.TheValue + (int)y;
return li;
}
private int _theValue = 0;
public int TheValue
{
get { return _theValue; }
set
{
if (value < MinValue)
_theValue = 0;
else
_theValue = value > MaxValue
? MaxValue
: value;
}
}
}
class Program
{
static void Main()
{
LimitedInt li1 = new LimitedInt();
LimitedInt li2 = new LimitedInt();
LimitedInt li3 = new LimitedInt();
li1.TheValue = 10; li2.TheValue = 26;
Console.WriteLine($" li1: { li1.TheValue }, li2: { li2.TheValue }");
li3 = -li1;
Console.WriteLine($"-{ li1.TheValue } = { li3.TheValue }");
li3 = li2 - li1;
Console.WriteLine($" { li2.TheValue } - { li1.TheValue } = { li3.TheValue }");
li3 = li1 - li2;
Console.WriteLine($" { li1.TheValue } - { li2.TheValue } = { li3.TheValue }");
}
}
// output
li1: 10, li2: 26
-10 = 0
26 - 10 = 16
10 - 26 = 0
9.15.2 运算符重载的限制
可重载的一元运算符:+、-、!、~、++、–、true、false。
可重载的二元运算符:+、-、*、/、%、&、|、^、<<、>>、==、!=、>、<、>=、<=。
运算符重载不能:
- 创建新运算符。
- 改变运算符的语法。
- 重新定义运算符如何处理预定义类型。
- 改变运算符的优先级或结合性。
对于 ++ 和 –- 运算符,重载只能提供一个方法体。当对对象使用前置或后置运算时,编译器将做出不同操作的处理:
- 前置运算:
- 直接在对象上执行重载代码。
- 返回对象。
- 后置运算:
- 首先浅拷贝对象。
- 在拷贝对象上执行重载代码。
- 返回拷贝对象。
因此,对引用类型(class)使用后置操作,返回结果将和前置操作一样,因为浅拷贝引用指向的对象就是原对象。但是对值类型(struct)使用后置操作不会出现这种现象。
最好对 struct 使用后置递增 / 递减,而不是 class。
using static System.Console;
public struct MyType // Run twice; once as a struct and again as a class.
// public class MyType
{
public int X;
public MyType( int x )
{
X = x;
}
public static MyType operator ++( MyType m )
{
m.X++;
return m;
}
}
class Test
{
static void Show( string message, MyType tv )
{
WriteLine( $"{message} {tv.X}" );
}
static void Main()
{
MyType tv = new MyType( 10 );
WriteLine( "Pre-increment" );
Show( "Before ", tv );
Show( "Returned ", ++tv );
Show( "After ", tv );
WriteLine();
tv = new MyType( 10 );
WriteLine( "Post-increment" );
Show( "Before ", tv );
Show( "Returned ", tv++ );
Show( "After ", tv );
}
}
// 结构体 output
Pre-increment
Before 10
Returned 11
After 11
Post-increment
Before 10
Returned 10
After 11
// 类 output
Pre-increment
Before 10
Returned 11
After 11
Post-increment
Before 10
Returned 11 Not what you would expect.
After 11
9.16 typeof 运算符
typeof 运算符返回参数对应的 System.Type 对象。
Type t = typeof ( SomeClass )
using System.Reflection; // Use the Reflection namespace to take full advantage
// of determining information about a type.
class SomeClass
{
public int Field1;
public int Field2;
public void Method1() { }
public int Method2() { return 1; }
}
class Program
{
static void Main()
{
Type t = typeof(SomeClass);
FieldInfo[] fi = t.GetFields();
MethodInfo[] mi = t.GetMethods();
foreach (FieldInfo f in fi)
Console.WriteLine($"Field : { f.Name }");
foreach (MethodInfo m in mi)
Console.WriteLine($"Method: { m.Name }");
}
}
// output
Field : Field1
Field : Field2
Method: Method1
Method: Method2
Method: ToString
Method: Equals
Method: GetHashCode
Method: GetType
GetType方法也会调用typeof运算符
class SomeClass
{
}
class Program
{
static void Main()
{
SomeClass s = new SomeClass();
Console.WriteLine($"Type s: { s.GetType().Name }");
}
}
// output
Type s: SomeClass
9.17 nameof 运算符
即使参数使用完全限定名,nameof 运算符也只返回其参数的非限定名称。
string var1 = "Local Variable";
Console.WriteLine (nameof (var1)); // Local Variable ("var1")
Console.WriteLine (nameof (MyClass)); // Class ("MyClass")
Console.WriteLine (nameof (MyClass.Method1)); // Public Method ("Method1")
Console.WriteLine (nameof (parameter1)); // Method Parameter ("parameter1")
Console.WriteLine (nameof (MyClass.Property1)); // Public Property ("Property1")
Console.WriteLine (nameof (MyClass.Field1)); // Public Field ("Field1")
Console.WriteLine (nameof (MyStruct)); // Struct ("MyStruct ")
9.18 其他运算符
- 空接合运算符
- 空条件运算符
更多推荐

所有评论(0)