Tip
本文是已了解至少一种编程语言并正在学习 C# 的开发人员的 “基础知识 ”部分的一部分。 如果你不熟悉编程,请先 学习入门 教程。
来自另一种语言? 本文中的大多数运算符(+、、-/&&||!%*!=<==>比较运算符和=)的工作方式与 Java、C++ 和 JavaScript 相同。 新人的主要惊喜是整数除法行为、前缀/后缀区别 ++/--以及复合赋值转换回左侧类型的方式。
运算符将一个或多个操作数合并为单个值。 你已经知道 C# 表达式中的表达式和运算符优先级;本文更深入地了解每天将使用的特定运算符。
算数运算符
五个算术运算符执行数值计算。
| Operator | Name | Example | Result |
|---|---|---|---|
+ |
附加内容 | 10 + 3 |
13 |
- |
减法 | 10 - 3 |
7 |
* |
乘法 | 10 * 3 |
30 |
/ |
除法 | 10 / 3 |
3 |
% |
余额 | 10 % 3 |
1 |
int apples = 10;
int oranges = 3;
Console.WriteLine(apples + oranges); // => 13 (addition)
Console.WriteLine(apples - oranges); // => 7 (subtraction)
Console.WriteLine(apples * oranges); // => 30 (multiplication)
Console.WriteLine(apples / oranges); // => 3 (integer division: truncates toward zero)
Console.WriteLine(apples % oranges); // => 1 (remainder)
// Integer division always truncates toward zero — the fractional part is discarded
int result = 7 / 2;
Console.WriteLine(result); // => 3, not 3.5
// Truncation applies to negative results too: -7 / 2 is -3, not -4
int negResult = -7 / 2;
Console.WriteLine(negResult); // => -3
// To get a decimal result, at least one operand must be a double or float
double precise = 7.0 / 2;
Console.WriteLine(precise); // => 3.5
// Remainder with negative operands: the sign of the result matches the dividend
Console.WriteLine(-7 % 3); // => -1 (-7 = 3 × -2 + (-1))
Console.WriteLine(7 % -3); // => 1 ( 7 = -3 × -2 + 1)
整数除法向零截断。 当两个操作数都是整数时, / 放弃小数部分: 7 / 2 是 3,而不是 3.5。 截断是朝零方向取整,而不是向较小的数取整:-7 / 2 是 -3(不是 -4)。 若要获取小数结果,请将至少一个操作数设置为浮点类型: 7.0 / 2 为 3.5。 这不同于某些语言,其中 / 始终生成浮点结果。
余数(%)表示整数除法后剩余的数:10 % 3是1,因为10 = 3 × 3 + 1。 它可用于循环访问固定范围(index % length)、测试可除性(n % 2 == 0)和提取数字。 对于负数操作数,结果的符号与 被除数(左操作数)的符号相同:-7 % 3 是 -1,而 7 % -3 是 1。
一元运算符
一元运算符对单个操作数执行操作。
int temperature = 20;
int windChill = -5;
int heatIndex = +temperature; // unary +: value unchanged (rarely needed)
int coldFactor = -windChill; // unary -: negates the value → 5
Console.WriteLine(heatIndex); // => 20
Console.WriteLine(coldFactor); // => 5
bool isRaining = false;
bool isSunny = !isRaining; // logical NOT: flips true/false
Console.WriteLine(isSunny); // => True
-
+x(一元加号)— 使值保持不变;很少显式写出,但这种写法是有效的。 -
-x(一元减数) - 否定值。 -
!x(逻辑非)— 将false翻转为false,并将true翻转为true。 你将经常使用!:if (!list.Contains(item))
递增和递减
++ 加 1 并 -- 减去 1。 两者都具有 前缀 形式和 后缀 形式,区别在于返回的是哪个值:
int counter = 5;
// Prefix: increment first, then use the new value
int a = ++counter;
Console.WriteLine(a); // => 6
Console.WriteLine(counter); // => 6
// Postfix: use the current value first, then increment
int b = counter++;
Console.WriteLine(b); // => 6 (value before increment)
Console.WriteLine(counter); // => 7 (incremented after)
// Decrement works the same way
int score = 10;
Console.WriteLine(score--); // => 10 (current value; score becomes 9)
Console.WriteLine(score); // => 9
-
前缀 (
++i,--i): 先递增或递减变量,然后返回 新 值。 -
后缀 (
i++, ):i--首先返回 当前 值,然后递增或递减变量。
当 -- 或 ++ 作为独立语句出现时(不是更大表达式的一部分),前缀和后缀具有相同的效果。 仅当使用结果时,区别才很重要,例如,在赋值或方法参数中。
关系运算符
关系运算符比较两个值并返回一个 bool。
| Operator | Meaning | Example |
|---|---|---|
< |
小于 | speed < limit |
> |
大于 | speed > limit |
<= |
小于或等于 | score <= 100 |
>= |
大于或等于 | score >= 0 |
int speed = 75;
int limit = 60;
Console.WriteLine(speed > limit); // => True (greater than)
Console.WriteLine(speed < limit); // => False (less than)
Console.WriteLine(speed >= limit); // => True (greater than or equal)
Console.WriteLine(speed <= limit); // => False (less than or equal)
// Relational operators work on all numeric types and char
// char comparison uses the character's numeric Unicode code point, not alphabetical position
// 'B' (U+0042, value 66) is less than 'A' (U+0041, value 65)? No — 'A' (65) < 'B' (66)
char grade = 'B';
Console.WriteLine(grade >= 'A' && grade <= 'C'); // => True ('A'=65 <= 'B'=66 <= 'C'=67)
关系运算符适用于所有数值类型和 char。 对于 char,比较依据字符的 Unicode 码位数值,而不是任何按字母顺序或特定领域排序的规则。 在上面的成绩示例中,'B' 大于或等于 'A',因为 'A' 的 Unicode 值为 66,而 'B' 的 Unicode 值为 65——数值决定比较结果,而不是字母成绩的含义。
相等运算符
== 和 != 用于检查两个值是否相等。
!= 在操作数 不 相等时为 true,相等时为 false。
int expected = 42;
int actual = 42;
Console.WriteLine(actual == expected); // => True (values are equal)
Console.WriteLine(actual != expected); // => False (true when values are not equal)
string name = "Alice";
Console.WriteLine(name == "Alice"); // => True (string content matches)
Console.WriteLine(name == "alice"); // => False (case-sensitive)
int x = 5;
Console.WriteLine(x == 10); // => False
对于数值类型, string相等性测试值。 对于引用类型,默认值为标识(两个变量是否指向同一对象),但许多类型包括 string 并 record 重写此项以比较内容。 如需全面了解相等性在值类型、引用类型、记录和结构体之间如何运作,请参阅 相等性比较。
注释
C# 没有 === 运算符。 写入 === 是编译时错误:
// This does not compile — C# has no === operator
bool same = (x === 10);
如果你熟悉 JavaScript,请使用 == 进行值比较(在 C# 中,== 对基元类型和字符串本来就是按值比较的)。 一个常见的相关错误是误把 =(赋值)写成了,而你本来想写的是 ==(相等性检查)。 编译器捕获最常见的形式,但仔细检查包含=的任何if条件。
条件逻辑运算符
&& (AND) 和 || (OR) 合并 bool 表达式。
int age = 20;
bool hasTicket = true;
// && (AND): both sides must be true
bool canEnter = age >= 18 && hasTicket;
Console.WriteLine(canEnter); // => True
// || (OR): at least one side must be true
bool freeEntry = age < 5 || age >= 65;
Console.WriteLine(freeEntry); // => False
// Short-circuit: right side is skipped when the result is already determined
// Here, items.Count is never called if items is null
List<string>? items = null;
bool hasItems = items != null && items.Count > 0;
Console.WriteLine(hasItems); // => False (short-circuits; no NullReferenceException)
这两个运算符都具有短路特性:当结果已确定时,它们会跳过对右操作数的求值。
- 一旦左侧为
false,&&就会返回false。 右侧永远不会被求值。 -
||一旦左侧为true,就返回true。 右侧永远不会被求值。
短路行为有一个实际好处:如上例所示,可以通过左侧的 null 检查,安全地为右侧的操作提供保护。 如果 null 是 items,&& 就会在此停止——items.Count 永远不会被调用,因此不会抛出 NullReferenceException。
条件运算符 ?:
条件运算符(也称为 三元 运算符)基于条件计算两个表达式之一:
condition ? value-when-true : value-when-false
int temperature2 = 35;
// condition ? value-when-true : value-when-false
string weather = temperature2 > 30 ? "hot" : "comfortable";
Console.WriteLine(weather); // => hot
// Only the matching branch evaluates — the other branch is never run
int divisor = 0;
// The division 10 / divisor is never evaluated because divisor == 0 is true
int safe = divisor == 0 ? -1 : 10 / divisor;
Console.WriteLine(safe); // => -1
?:运算符始终只计算一个分支 , 与条件不匹配的一侧永远不会计算。 这使得在一侧使用某个对其他输入会失败的表达式也是安全的,只要条件能够正确地对其进行保护。
对于简单的内联选项,请使用 ?:。 对于多向条件或代码块, if/else 语句通常更清晰。
赋值运算符
简单赋值运算符 = 将值存储在变量中:
int level = 1; // declaration + initialization
level = 5; // reassignment
C# 中的赋值是 右关联,这意味着 a = b = c = 0 计算从右到左: c 获取 0,然后 b 获取 0,然后 a 获取 0。
复合赋值
复合赋值运算符将二元运算与赋值相结合:
| Operator | 等效于 |
|---|---|
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 |
int level = 1;
level = 5; // simple assignment: replaces the value
Console.WriteLine(level); // => 5
// Compound assignment: short form of binary operation + assignment
int hp = 100;
hp += 20; // same as: hp = hp + 20
Console.WriteLine(hp); // => 120
hp -= 10; // same as: hp = hp - 10
Console.WriteLine(hp); // => 110
hp *= 2; // same as: hp = hp * 2
Console.WriteLine(hp); // => 220
hp /= 3; // same as: hp = hp / 3 (integer division)
Console.WriteLine(hp); // => 73
hp %= 7; // same as: hp = hp % 7
Console.WriteLine(hp); // => 3
复合赋值不仅仅是一种简化写法。 它对左侧表达式只求值一次,然后将结果转换回左侧表达式的类型。 当左侧具有副作用时(例如数组索引器),这一点很重要;这也是为什么对 byte 变量进行复合赋值时无需显式强制转换即可通过编译,而展开后的写法却不能:
// Assignment is right-associative: evaluated right to left
int a2, b2, c2;
a2 = b2 = c2 = 0; // c2 = 0 first, then b2 = 0, then a2 = 0
Console.WriteLine($"{a2} {b2} {c2}"); // => 0 0 0
// Compound assignment evaluates the left side once and converts back to the LHS type
byte small = 200;
small += 10; // equivalent to: small = (byte)(small + 10); result is 210
Console.WriteLine(small); // => 210
small += 10 编译是因为编译器会自动插入缩小转换 ( 结果 210,适合 byte 0-255 的范围)。
small = small + 10 需要进行显式的 (byte) 强制转换,因为算术运算会将两个操作数提升为 int。
其他 C# 运算符
本文介绍在日常代码中遇到的大多数运算符。 C# 语言包含适用于特定方案的更多运算符:
-
移位运算符(
<<,>>>,>>)——将整数值的各个位向左或向右移动指定的位数。 按位和整数逻辑运算符(&、、|、~^) — 一次合并或反转整数值,在标志、掩码和低级别代码中很有用:按位和移位运算符 -
checked以及unchecked— 控制整数溢出是否引发异常(checked)或以无提示方式包装(unchecked): 已选中且未选中 -
空运算符(
??、?.、??=、?[])——通过提供默认值或短路成员访问来安全处理null值:空运算符 -
类型测试和转换运算符(
is、as、typeof、强制转换(T))——检查或转换值的运行时类型:类型测试和强制转换运算符 -
范围和索引运算符 (
..,^) — 用于创建范围和相对于末尾的索引,以便对数组和跨度进行切片:成员访问和空条件运算符 - 解构赋值 - 将元组或类型解压缩到单个表达式中的单个变量: 解构元组和其他类型
另见
- C# 表达式 — 表达式的构成方式以及运算符优先级如何发挥作用
-
相等比较——
!=、Equals和==在不同类型之间如何运作 - C# 运算符和表达式 (语言参考) - 完全优先表和每个运算符