C#运算符与表达式终极指南:从入门到精通的万字长文

52 阅读2分钟

一、什么是运算符与表达式?

  • 运算符(Operator):是一种特殊的符号,用于执行特定的数学、逻辑或位运算。例如 + 是加法运算符,== 是相等运算符。
  • 操作数(Operand):是参与运算的数据。例如,在 5 + 3 中,53 就是操作数。
  • 表达式(Expression):是由操作数和运算符组成的序列,其计算结果会产生一个新值。例如 5 + 3 是一个表达式,它的计算结果是 8x > y 也是一个表达式,它的计算结果是 truefalse

表达式是C#程序的基本执行单元。 理解它们,就是理解程序如何思考。


二、基础运算符

1. 算术运算符

它们负责处理基本的数学计算,与你在小学数学课上学到的几乎一样。

运算符名称示例结果
+加法10 + 515
-减法10 - 55
*乘法10 * 550
/除法10 / 52
%取模 (求余数)10 % 31
int a = 10;
int b = 3;

Console.WriteLine($"加法: {a + b}");       // 输出: 13
Console.WriteLine($"减法: {a - b}");       // 输出: 7
Console.WriteLine($"乘法: {a * b}");       // 输出: 30
Console.WriteLine($"除法: {a / b}");       // 输出: 3 (注意:整数除法)
Console.WriteLine($"取模: {a % b}");       // 输出: 1

注意事项:整数除法

当两个整数相除时,结果也是一个整数,小数部分会被直接截断(不是四舍五入)。例如 10 / 3 的结果是 3。如果你想得到精确的小数结果,至少要有一个操作数是浮点类型。

double result = 10.0 / 3; // 结果是 3.333...
double result2 = (double)10 / 3; // 结果也是 3.333...

2. 赋值运算符

赋值运算符用于给变量分配一个值。最基本的是 =,但C#提供了一系列复合赋值运算符,让代码更简洁。

运算符示例等价于
=x = 5x = 5
+=x += 5x = x + 5
-=x -= 5x = x - 5
*=x *= 5x = x * 5
/=x /= 5x = x / 5
%=x %= 5x = x % 5
int score = 100;
score += 10; // score 现在是 110
score -= 20; // score 现在是 90
score *= 2;  // score 现在是 180
score /= 3;  // score 现在是 60

使用复合赋值运算符不仅代码更短,而且可读性更好,是专业代码的标志之一。

3. 一元运算符

这些运算符只需要一个操作数。

运算符名称示例描述
+正号+5表示一个正数 (通常省略)
-负号-5表示一个负数 (取反)
++递增x++++x将变量的值加1
--递减x----x将变量的值减1

++-- 有两种形式:前缀后缀,它们的区别在于表达式求值的时机

  • 前缀 (Prefix) ++x: 先将 x 的值加1,然后返回加1后的值。
  • 后缀 (Postfix) x++: 先返回 x原始值,然后再将 x 的值加1。
int i = 5;
int j = 5;

// 前缀:先自增,再赋值
int prefixResult = ++i; // i 变成 6, prefixResult 也被赋值为 6
Console.WriteLine($"i: {i}, prefixResult: {prefixResult}"); // 输出: i: 6, prefixResult: 6

// 后缀:先赋值,再自增
int postfixResult = j++; // postfixResult 被赋值为 5, 然后 j 变成 6
Console.WriteLine($"j: {j}, postfixResult: {postfixResult}"); // 输出: j: 6, postfixResult: 5

三、逻辑与比较

1. 关系运算符

也叫比较运算符,用于比较两个操作数,其结果总是一个布尔值 (truefalse)。

运算符名称示例结果为true的条件
==等于a == ba 和 b 的值相等
!=不等于a != ba 和 b 的值不相等
>大于a > ba 的值大于 b
<小于a < ba 的值小于 b
>=大于等于a >= ba 的值大于或等于 b
<=小于等于a <= ba 的值小于或等于 b
int age = 20;
bool isAdult = age >= 18; // isAdult 的值为 true
bool isVoter = age == 21; // isVoter 的值为 false

这些运算符是 if 语句、while 循环等所有控制流语句的核心。

2. 逻辑运算符

用于组合多个布尔表达式,构建更复杂的判断逻辑。

运算符名称示例描述
!逻辑非 (NOT)!isValid如果操作数为true,结果为false;反之亦然
&&逻辑与 (AND)isLogin && isAdmin两个操作数都为true时,结果才为true
``逻辑或 (OR)`isMemberisVIP`只要有一个操作数为true,结果就为true

短路求值 (Short-circuiting)

&&|| 有一个非常重要的特性叫“短路”。

  • 对于 expr1 && expr2:如果 expr1 的计算结果为 false,那么整个表达式的结果必定是 false,此时 expr2 将不会被计算
  • 对于 expr1 || expr2:如果 expr1 的计算结果为 true,那么整个表达式的结果必定是 true,此时 expr2 将不会被计算

这个特性非常有用,常用于避免空引用异常或减少不必要的计算:

string name = null;

// 如果不使用短路,当 name 为 null 时,name.Length 会抛出 NullReferenceException
// if (name != null & name.Length > 0) { ... }  // 注意这里是 &,非短路

// 使用短路 &&,当 name 为 null 时,第一个条件为 false,第二个条件根本不会执行,非常安全
if (name != null && name.Length > 0)
{
    Console.WriteLine("Name is not empty.");
}

四、位运算符

运算符名称描述
&按位与两个操作数中,对应位都为1时,结果位才为1
``按位或两个操作数中,对应位只要有一个为1,结果位就为1
^按位异或两个操作数中,对应位不同时,结果位为1
~按位取反单目运算符,将操作数的所有位反转 (0变1,1变0)
<<左移将操作数的所有位向左移动指定的位数,右侧补0
>>右移将操作数的所有位向右移动指定的位数

应用场景:权限管理 枚举经常与位运算符结合,用于管理一组开关状态(Flags)。

[Flags]
public enum Permissions
{
    None = 0,       // 0000
    Read = 1,       // 0001
    Write = 2,      // 0010
    Execute = 4,    // 0100
    All = Read | Write | Execute // 0111
}

Permissions userPermissions = Permissions.Read | Permissions.Write;

// 检查是否包含写权限
if ((userPermissions & Permissions.Write) == Permissions.Write)
{
    Console.WriteLine("User has Write permission.");
}

// 添加执行权限
userPermissions |= Permissions.Execute;

// 移除写权限
userPermissions &= ~Permissions.Write;

五、那些强大的“语法糖”

随着C#语言的演进,出现了许多新的运算符,它们极大地简化了代码,使其更具表现力和健壮性。

1. Null 合并运算符 (????=)

用于处理 null 值的利器。

  • ?? (Null-Coalescing Operator): a ?? b 如果 a 不为 null,则表达式的结果是 a;如果 anull,则结果是 b
string userName = null;
string displayName = userName ?? "Guest"; // displayName 的值将是 "Guest"

string userName2 = "Admin";
string displayName2 = userName2 ?? "Guest"; // displayName2 的值将是 "Admin"

这完美地替代了冗长的 if 或三元表达式 (userName != null) ? userName : "Guest"

  • ??= (Null-Coalescing Assignment Operator) (C# 8.0+) variable ??= value 仅当 variablenull 时,才将 value 赋给 variable
List<int> numbers = null;
numbers ??= new List<int>(); // 因为 numbers 是 null,所以为其创建一个新实例

numbers.Add(1);

numbers ??= new List<int>(); // 因为 numbers 不再是 null,所以这条语句什么也不做

这对于延迟初始化(Lazy Initialization)非常有用。

2. Null 条件运算符 (?.?[])

用于优雅地避免 NullReferenceException,告别层层嵌套的 if (obj != null) 检查。

  • ?. (Null-Conditional Member Access): 在访问对象成员(方法或属性)之前,检查对象是否为 null。如果是 null,整个表达式直接返回 null,而不会抛出异常。
string street = "";

User user = null; // GetUser();


// 传统方式,需要层层检查
if (user != null)
{
    if (user.UserAddress != null)
    {
        street = user.UserAddress.Street;
    }
}

// 使用 ?. 运算符,一行搞定!
// 如果 user 或 user.UserAddress 是 null,street 将被赋值为 null
string streetElegant = user?.UserAddress?.Street;


public class User { public Address UserAddress { get; set; } }
public class Address { public string Street { get; set; } }
  • ?[] (Null-Conditional Element Access): 用于访问数组或索引器。
List<string> names = null;
string firstName = names?[0]; // 如果 names 为 null,firstName 为 null,不抛异常

3. 条件运算符 (?:) - 三元表达式

它是 if-else 语句的紧凑形式,适用于简单的条件赋值。

语法: condition ? first_expression : second_expression; 如果 conditiontrue,则计算 first_expression 并将其作为结果;否则计算 second_expression

int age = 20;
string status = (age >= 18) ? "Adult" : "Minor"; // status 的值为 "Adult"

4. 类型相关运算符

运算符名称描述
is类型判断检查对象是否与给定类型兼容,返回布尔值。C# 7.0+支持模式匹配。
as类型转换尝试将对象转换为指定类型,如果转换失败,返回null而不是抛出异常。
typeof获取类型返回一个表示类型的 System.Type 对象。
sizeof获取大小(仅限非托管类型)返回给定类型值在内存中占用的字节数。
object obj = "Hello World";

if (obj is string)
{
    Console.WriteLine("It's a string.");
}

if (obj is string s) // 如果是string,则直接转换并赋值给 s
{
    Console.WriteLine($"The string has {s.Length} characters.");
}

string str = obj as string; // 转换成功,str 为 "Hello World"
StringBuilder sb = obj as StringBuilder; // 转换失败,sb 为 null

六、运算符的规则 - 优先级与结合性

当一个表达式中包含多个运算符时,谁先计算?这就是**优先级(Precedence)结合性(Associativity)**要解决的问题。

  • 优先级:决定了不同运算符的计算顺序。例如,*/ 的优先级高于 +-,所以 2 + 3 * 4 的结果是 14 而不是 20
  • 结合性:当多个具有相同优先级的运算符在一起时,决定它们的计算方向。
    • 左结合性 (Left-associative):从左到右计算。例如 a - b - c 等价于 (a - b) - c。大多数二元运算符都是左结合的。
    • 右结合性 (Right-associative):从右到左计算。例如赋值运算符 a = b = c 等价于 a = (b = c)。三元运算符也是右结合的。

C#运算符优先级(由高到低摘录)

  1. 主要: x.y, f(x), a[i], x++, x--, new, typeof, sizeof
  2. 一元: +, -, !, ~, ++x, --x, (T)x
  3. 乘法: *, /, %
  4. 加法: +, -
  5. 移位: <<, >>
  6. 关系: <, >, <=, >=, is, as
  7. 相等: ==, !=
  8. 位与: &
  9. 位异或: ^
  10. 位或: |
  11. 逻辑与: &&
  12. 逻辑或: ||
  13. Null合并: ??
  14. 条件: ?:
  15. 赋值与Lambda: =, *=, /=, +=, -=, =>

结语

点个赞,关注我获取更多实用 C# 技术干货!如果觉得有用,记得收藏本文!