C# - 运算符优先级
C# - 运算符优先级与结合性
Section titled “C# - 运算符优先级与结合性”在 C# 中,运算符优先级决定了包含多个运算符的表达式中操作的执行顺序。可以将其类比为数学中的“运算顺序”(如 PEMDAS/BODMAS)。优先级较高的运算符会在优先级较低的运算符之前进行求值。
例如,在表达式 var result = 10 + 5 * 2; 中,乘法运算符 * 的优先级高于加法运算符 +。因此,会首先计算 5 * 2(结果为 10),然后再计算 10 + 10。最终赋给 result 的值为 20,而不是 30。
当运算符具有相同的优先级时,它们的结合性决定了求值的顺序。大多数运算符是左结合的(从左到右求值),而少数运算符,如赋值运算符和空值合并运算符,是右结合的(从右到左求值)。
C# 运算符优先级表
Section titled “C# 运算符优先级表”下表列出了 C# 运算符,从最高优先级到最低优先级。同一类别内的运算符具有相同的优先级。
| 优先级 | 类别 | 运算符 | 结合性 |
|---|---|---|---|
| 1 (最高) | 主要运算符 | x.y, f(x), a[i], x?.y, x?[i], x++, x--, new, typeof, sizeof | 从左到右 |
| 2 | 一元运算符 | +, -, !, ~, ++x, --x, (T)x, ^ (从末尾索引) | 从右到左 |
| 3 | 范围运算符 | .. | 从左到右 |
| 4 | 乘法运算符 | *, /, % | 从左到右 |
| 5 | 加法运算符 | +, - | 从左到右 |
| 6 | 位移运算符 | <<, >>, >>> (无符号右移) | 从左到右 |
| 7 | 关系与类型测试运算符 | <, >, <=, >=, is, as | 从左到右 |
| 8 | 相等性运算符 | ==, != | 从左到右 |
| 9 | 按位与 | & | 从左到右 |
| 10 | 按位异或 | ^ | 从左到右 |
| 11 | 按位或 | | | 从左到右 |
| 12 | 逻辑与 | && | 从左到右 |
| 13 | 逻辑或 | || | 从左到右 |
| 14 | 空值合并运算符 | ?? | 从右到左 |
| 15 | 条件(三元)运算符 | ?: | 从右到左 |
| 16 (最低) | 赋值与 Lambda 运算符 | =, *=, /=, %=, +=, -=, <<=, >>=, &=, ^=, |=, ??=, => | 从右到左 |
示例 1:优先级(乘法 vs. 加法)
Section titled “示例 1:优先级(乘法 vs. 加法)”乘法运算符 * 在加法运算符 + 之前进行求值。
// 现代 C# 的顶级语句int result = 10 + 5 * 2; // 求值顺序为 10 + (5 * 2)Console.WriteLine($"Result: {result}"); // 输出: Result: 20示例 2:使用括号覆盖优先级
Section titled “示例 2:使用括号覆盖优先级”括号 () 具有最高的优先级,可用于显式控制求值顺序。
int result = (10 + 5) * 2; // 括号强制加法运算优先执行Console.WriteLine($"Result: {result}"); // 输出: Result: 30示例 3:结合性(赋值)
Section titled “示例 3:结合性(赋值)”赋值运算符 = 是右结合的。表达式从右到左进行求值。
int a, b, c;
// 从右到左求值:c 被赋值为 10,然后 b 被赋值为 c 的值,然后 a 被赋值为 b 的值。a = b = c = 10;
Console.WriteLine($"a: {a}, b: {b}, c: {c}"); // 输出: a: 10, b: 10, c: 10最佳实践与常见陷阱
Section titled “最佳实践与常见陷阱”疑虑时使用括号
Section titled “疑虑时使用括号”即使你了解优先级规则,你的同事可能不了解。使用括号可以明确表达代码意图,并减轻读者的认知负担。这在团队环境中是编写可维护代码的关键实践。
推荐用法: var isValid = (user.IsLoggedIn && user.HasPermissions) || user.IsAdmin;
不够清晰: var isValid = user.IsLoggedIn && user.HasPermissions || user.IsAdmin;
常见陷阱:& 与 &&
Section titled “常见陷阱:& 与 &&”小心不要混淆按位与运算符 & 和逻辑与运算符 &&。虽然两者都可用于布尔表达式,但 && 具有“短路”特性——如果左侧为 false,它就不会评估右侧。这更安全也更高效。而按位 & 总是会评估两侧。
string name = null;
// 这将抛出 NullReferenceException,因为 `&` 会评估两侧。// bool isInvalid = name != null & name.Length > 0;
// 这可以正确工作,因为 `&&` 具有短路特性。// 如果 name 为 null,则不会评估第二部分。bool isValid = name != null && name.Length > 0;Console.WriteLine($"Is valid: {isValid}"); // 输出: Is valid: False常见问题 (FAQ)
Section titled “常见问题 (FAQ)”问题 1:在 C# 中,哪些运算符具有最高的优先级?
主要运算符,例如括号 ()、成员访问 .、方法调用 f() 和数组索引 [] 具有最高的优先级。
问题 2:当两个运算符具有相同的优先级时会发生什么?
结合性决定了它们的顺序。例如,* 和 / 具有相同的优先级并且是左结合的,因此 100 / 10 * 2 会被评估为 (100 / 10) * 2,结果为 20。
问题 3:如何避免运算符优先级造成的混淆?
避免歧义和错误的最佳方法是使用括号 () 来显式分组操作。这使你的代码具有自解释性,并且更容易让所有人理解。