C# 提供了许多运算符。 其中许多都受到内置类型的支持,可用于对这些类型的值执行基本操作。 这些运算符包括以下组:
- 算术运算符,将对数值操作数执行算术运算
- 比较运算符,将比较数值操作数
- 布尔逻辑运算符,将对
bool
操作数执行逻辑运算- 位运算符和移位运算符,将对整数类型的操作数执行位运算或移位运算
- 相等运算符,将检查其操作数是否相等
通常可以重载这些运算符,也就是说,可以为用户定义类型的操作数指定运算符行为。
以下运算符对数值类型的操作数执行算术运算:
一元增量运算符 ++
按 1 递增其操作数。
增量运算符以两种形式进行支持:后缀增量运算符 x++
和前缀增量运算符 ++x
。
x++
的结果是此操作前的 x
的值,如以下示例所示:
int i = 3;
Console.WriteLine(i); // output: 3
Console.WriteLine(i++); // output: 3
Console.WriteLine(i); // output: 4
++x
的结果是此操作后的 x
的值,如以下示例所示:
double a = 1.5;
Console.WriteLine(a); // output: 1.5
Console.WriteLine(++a); // output: 2.5
Console.WriteLine(a); // output: 2.5
一元减量运算符 --
按 1 递减其操作数。
减量运算符以两种形式进行支持:后缀减量运算符x--
和前缀减量运算符 --x
。
x--
的结果是此操作前的 x
的值,如以下示例所示:
int i = 3;
Console.WriteLine(i); // output: 3
Console.WriteLine(i--); // output: 3
Console.WriteLine(i); // output: 2
--x
的结果是此操作后的 x
的值,如以下示例所示:
double a = 1.5;
Console.WriteLine(a); // output: 1.5
Console.WriteLine(--a); // output: 0.5
Console.WriteLine(a); // output: 0.5
一元 +
运算符返回其操作数的值。 一元 -
运算符对其操作数的数值取负。
Console.WriteLine(+4); // output: 4
Console.WriteLine(-4); // output: -4
Console.WriteLine(-(-4)); // output: 4
uint a = 5;
var b = -a;
Console.WriteLine(b); // output: -5
Console.WriteLine(b.GetType()); // output: System.Int64
Console.WriteLine(-double.NaN); // output: NaN
ulong 类型不支持一元 -
运算符。
乘法运算符 *
计算其操作数的乘积:
Console.WriteLine(5 * 2); // output: 10
Console.WriteLine(0.5 * 2.5); // output: 1.25
Console.WriteLine(0.1m * 23.4m); // output: 2.34
除法运算符 /
用它的左侧操作数除以右侧操作数。
对于整数类型的操作数,/
运算符的结果为整数类型,并且等于两个操作数之商向零舍入后的结果:
Console.WriteLine(13 / 5); // output: 2
Console.WriteLine(-13 / 5); // output: -2
Console.WriteLine(13 / -5); // output: -2
Console.WriteLine(-13 / -5); // output: 2
若要获取浮点数形式的两个操作数之商,请使用 float
、double
或 decimal
类型:
Console.WriteLine(13 / 5.0); // output: 2.6
int a = 13;
int b = 5;
Console.WriteLine((double)a / b); // output: 2.6
对于 float
、double
和 decimal
类型,/
运算符的结果为两个操作数之商:
Console.WriteLine(16.8f / 4.1f); // output: 4.097561
Console.WriteLine(16.8d / 4.1d); // output: 4.09756097560976
Console.WriteLine(16.8m / 4.1m); // output: 4.0975609756097560975609756098
如果操作数之一为 decimal
,那么另一个操作数不得为 float
和 double
,因为 float
和 double
都无法隐式转换为 decimal
。 必须将 float
或 double
操作数显式转换为 decimal
类型。 如需详细了解数值类型之间的转换,请参阅内置数值转换。
余数运算符 %
计算左侧操作数除以右侧操作数后的余数。
于整数类型的操作数,a % b
的结果是 a - (a / b) * b
得出的值。 非零余数的符号与左侧操作数的符号相同,如下例所示:
Console.WriteLine(5 % 4); // output: 1
Console.WriteLine(5 % -4); // output: 1
Console.WriteLine(-5 % 4); // output: -1
Console.WriteLine(-5 % -4); // output: -1
对于 float
和 double
操作数,有限的 x
和 y
的 x % y
的结果是值 z
,因此
z
(如果不为零)的符号与 x
的符号相同。z
的绝对值是 |x| - n * |y|
得出的值,其中 n
是小于或等于 |x| / |y|
的最大可能整数,|x|
和 |y|
分别是 x
和 y
的绝对值。Console.WriteLine(-5.2f % 2.0f); // output: -1.2
Console.WriteLine(5.9 % 3.1); // output: 2.8
Console.WriteLine(5.9m % 3.1m); // output: 2.8
加法运算符 +
计算其操作数的和,减法运算符 -
从其左侧操作数中减去其右侧操作数:
Console.WriteLine(5 + 4); // output: 9
Console.WriteLine(5 + 4.3); // output: 9.3
Console.WriteLine(5.1m + 4.2m); // output: 9.3
Console.WriteLine(47 - 3); // output: 44
Console.WriteLine(5 - 4.3); // output: 0.7
Console.WriteLine(7.5m - 2.3m); // output: 5.2
以下列表对优先级由高到低的顺序对算术运算符进行排序:
x++
和减量 x--
运算符++x
和减量 --x
以及一元 +
和 -
运算符*
、/
和 %
运算符+
和 -
运算符以下运算符使用 bool 操作数执行逻辑运算:
- 一元
!
(逻辑非)运算符。- 二元
&
(逻辑与)、|
(逻辑或)和^
(逻辑异或)运算符。 这些运算符始终计算两个操作数。- 二元
&&
(条件逻辑与)和||
(条件逻辑或)运算符。 这些运算符仅在必要时才计算右侧操作数。对于整型数值类型的操作数,
&
、|
和^
运算符执行位逻辑运算。 有关详细信息,请参阅位运算符和移位运算符。
一元前缀 !
运算符计算操作数的逻辑非。
bool passed = false;
Console.WriteLine(!passed); // output: True
Console.WriteLine(!true); // output: False
&
运算符计算操作数的逻辑与。如果 x
和 y
的计算结果都为 true
,则 x & y
的结果为 true
。 否则,结果为 false
。
即使左侧操作数计算结果为 false
,&
运算符也会计算这两个操作数,而在这种情况下,无论右侧操作数的值为何,运算结果都为 false
。
^
运算符计算操作数的逻辑异或(亦称为“逻辑 XOR”)。 如果 x
计算结果为 true
且 y
计算结果为 false
,或者 x
计算结果为 false
且 y
计算结果为 true
,那么 x ^ y
的结果为 true
。 否则,结果为 false
。
|
运算符计算操作数的逻辑或。 如果 x
或 y
的计算结果为 true
,则 x | y
的结果为 true
。 否则,结果为 false
。
即使左侧操作数计算结果为 true
,|
运算符也会计算这两个操作数,而在这种情况下,无论右侧操作数的值为何,运算结果都为 true
。
条件逻辑与运算符 &&
(亦称为“短路”逻辑与运算符)计算操作数的逻辑与。 如果 x
和 y
的计算结果都为 true
,则 x && y
的结果为 true
。 否则,结果为 false
。 如果 x
的计算结果为 false
,则不计算 y
。
条件逻辑或运算符 ||
(亦称为“短路”逻辑或运算符)计算操作数的逻辑或。 如果 x
或 y
的计算结果为 true
,则 x || y
的结果为 true
。 否则,结果为 false
。 如果 x
的计算结果为 true
,则不计算 y
。
以下列表按优先级从高到低的顺序对逻辑运算符进行排序:
!
&
^
|
&&
||
使用括号 ()
可以更改运算符优先级决定的计算顺序。
这些运算符是针对
int
、uint
、long
和ulong
类型定义的。
~
运算符通过反转每个位产生其操作数的按位求补:
uint a = 0b_0000_1111_0000_1111_0000_1111_0000_1100;
uint b = ~a;
Console.WriteLine(Convert.ToString(b, toBase: 2));
// Output:
// 11110000111100001111000011110011
<<
运算符将其左侧操作数向左移动右侧操作数定义的位数。
左移运算会放弃超出结果类型范围的高阶位,并将低阶空位位置设置为零,如以下示例所示:
uint x = 0b_1100_1001_0000_0000_0000_0000_0001_0001;
Console.WriteLine($"Before: {Convert.ToString(x, toBase: 2)}");
uint y = x << 4;
Console.WriteLine($"After: {Convert.ToString(y, toBase: 2)}");
// Output:
// Before: 11001001000000000000000000010001
// After: 10010000000000000000000100010000
由于移位运算符仅针对 int
、uint
、long
和 ulong
类型定义,因此运算的结果始终包含至少 32 位。 如果左侧操作数是其他整数类型(sbyte
、byte
、short
、ushort
或 char
),则其值将转换为 int
类型,如以下示例所示:
byte a = 0b_1111_0001;
var b = a << 8;
Console.WriteLine(b.GetType());
Console.WriteLine($"Shifted byte: {Convert.ToString(b, toBase: 2)}");
// Output:
// System.Int32
// Shifted byte: 1111000100000000
>>
运算符将其左侧操作数向右移动右侧操作数定义的位数。
右移位运算会放弃低阶位,如以下示例所示:
uint x = 0b_1001;
Console.WriteLine($"Before: {Convert.ToString(x, toBase: 2), 4}");
uint y = x >> 2;
Console.WriteLine($"After: {Convert.ToString(y, toBase: 2), 4}");
// Output:
// Before: 1001
// After: 10
高顺序空位位置是根据左侧操作数类型设置的,如下所示:
int
或 long
,则右移运算符将执行 算术移位:左侧操作数的最高有效位(符号位)的值将传播到高顺序空位位置。 也就是说,如果左侧操作数为非负,高顺序空位位置设置为零,如果为负,则将该位置设置为 1。 int a = int.MinValue;
Console.WriteLine($"Before: {Convert.ToString(a, toBase: 2)}");
int b = a >> 3;
Console.WriteLine($"After: {Convert.ToString(b, toBase: 2)}");
// Output:
// Before: 10000000000000000000000000000000
// After: 11110000000000000000000000000000
uint
或 ulong
,则右移运算符执行逻辑移位:高顺序空位位置始终设置为零。 uint c = 0b_1000_0000_0000_0000_0000_0000_0000_0000;
Console.WriteLine($"Before: {Convert.ToString(c, toBase: 2), 32}");
uint d = c >> 3;
Console.WriteLine($"After: {Convert.ToString(d, toBase: 2), 32}");
// Output:
// Before: 10000000000000000000000000000000
// After: 10000000000000000000000000000
如果操作数相等,等于运算符 ==
返回 true
,否则返回 false
。
如果内置值类型的值相等,则其操作数相等:
int a = 1 + 2 + 3;
int b = 6;
Console.WriteLine(a == b); // output: True
char c1 = 'a';
char c2 = 'A';
Console.WriteLine(c1 == c2); // output: False
Console.WriteLine(c1 == char.ToLower(c2)); // output: True
默认情况下,如果两个非记录引用类型操作符引用同一对象,则这两个操作符相等:
public class ReferenceTypesEquality
{
public class MyClass
{
private int id;
public MyClass(int id) => this.id = id;
}
public static void Main()
{
var a = new MyClass(1);
var b = new MyClass(1);
var c = a;
Console.WriteLine(a == b); // output: False
Console.WriteLine(a == c); // output: True
}
}
在 C# 9.0 和更高版本中提供,记录类型支持 ==
和 !=
运算符,这些运算符默认提供值相等性语义。 也就是说,当两个记录操作数均为 null
或所有字段的对应值和自动实现的属性相等时,两个记录操作数都相等。
public class RecordTypesEquality
{
public record Point(int X, int Y, string Name);
public record TaggedNumber(int Number, List<string> Tags);
public static void Main()
{
var p1 = new Point(2, 3, "A");
var p2 = new Point(1, 3, "B");
var p3 = new Point(2, 3, "A");
Console.WriteLine(p1 == p2); // output: False
Console.WriteLine(p1 == p3); // output: True
var n1 = new TaggedNumber(2, new List<string>() { "A" });
var n2 = new TaggedNumber(2, new List<string>() { "A" });
Console.WriteLine(n1 == n2); // output: False
}
}
如果两个字符串均为 null
或者两个字符串实例具有相等长度且在每个字符位置有相同字符,则这两个字符串]操作数相等:
string s1 = "hello!";
string s2 = "HeLLo!";
Console.WriteLine(s1 == s2.ToLower()); // output: True
string s3 = "Hello!";
Console.WriteLine(s1 == s3); // output: False
如果操作数不相等,不等于运算符 !=
返回 true
,否则返回 false
。 对于内置类型的操作数,表达式 x != y
生成与表达式 !(x == y)
相同的结果。
int a = 1 + 1 + 2 + 3;
int b = 6;
Console.WriteLine(a != b); // output: True
string s1 = "Hello";
string s2 = "Hello";
Console.WriteLine(s1 != s2); // output: False
object o1 = 1;
object o2 = 1;
Console.WriteLine(o1 != o2); // output: True
<
(小于)、>
(大于)、<=
(小于或等于)和>=
(大于或等于)比较(也称为关系)运算符比较其操作数。 所有整型和浮点数值类型都支持这些运算符。
如果左侧操作数小于右侧操作数,<
运算符返回 true
,否则返回 false
:
Console.WriteLine(7.0 < 5.1); // output: False
Console.WriteLine(5.1 < 5.1); // output: False
Console.WriteLine(0.0 < 5.1); // output: True
Console.WriteLine(double.NaN < 5.1); // output: False
Console.WriteLine(double.NaN >= 5.1); // output: False
如果左侧操作数大于右侧操作数,>
运算符返回 true
,否则返回 false
:
Console.WriteLine(7.0 > 5.1); // output: True
Console.WriteLine(5.1 > 5.1); // output: False
Console.WriteLine(0.0 > 5.1); // output: False
Console.WriteLine(double.NaN > 5.1); // output: False
Console.WriteLine(double.NaN <= 5.1); // output: False
如果左侧操作数小于或等于右侧操作数,<=
运算符返回 true
,否则返回 false
:
Console.WriteLine(7.0 <= 5.1); // output: False
Console.WriteLine(5.1 <= 5.1); // output: True
Console.WriteLine(0.0 <= 5.1); // output: True
Console.WriteLine(double.NaN > 5.1); // output: False
Console.WriteLine(double.NaN <= 5.1); // output: False
如果左侧操作数大于或等于右侧操作数,>=
运算符返回 true
,否则返回 false
:
Console.WriteLine(7.0 >= 5.1); // output: True
Console.WriteLine(5.1 >= 5.1); // output: True
Console.WriteLine(0.0 >= 5.1); // output: False
Console.WriteLine(double.NaN < 5.1); // output: False
Console.WriteLine(double.NaN >= 5.1); // output: False
default value 表达式生成类型的默认值。 有两种类型的 default value 表达式:default 运算符调用和 default 文本。
default
运算符的实参必须是类型或类型形参的名称,如以下示例所示:
Console.WriteLine(default(int)); // output: 0
Console.WriteLine(default(object) is null); // output: True
void DisplayDefaultOf<T>()
{
var val = default(T);
Console.WriteLine($"Default value of {typeof(T)} is {(val == null ? "null" : val.ToString())}.");
}
DisplayDefaultOf<int?>();
DisplayDefaultOf<System.Numerics.Complex>();
DisplayDefaultOf<System.Collections.Generic.List<int>>();
// Output:
// Default value of System.Nullable`1[System.Int32] is null.
// Default value of System.Numerics.Complex is (0, 0).
// Default value of System.Collections.Generic.List`1[System.Int32] is null.
nameof
表达式可生成变量、类型或成员的名称作为字符串常量
Console.WriteLine(nameof(System.Collections.Generic)); // output: Generic
Console.WriteLine(nameof(List<int>)); // output: List
Console.WriteLine(nameof(List<int>.Count)); // output: Count
Console.WriteLine(nameof(List<int>.Add)); // output: Add
var numbers = new List<int> { 1, 2, 3 };
Console.WriteLine(nameof(numbers)); // output: numbers
Console.WriteLine(nameof(numbers.Count)); // output: Count
Console.WriteLine(nameof(numbers.Add)); // output: Add
见后续Lambda表式专门培训章节