08-运算符
运算符是编程中的基础工具,用于对变量进行操作。C# 提供了丰富的运算符,涵盖算术运算、逻辑判断、赋值操作、类型检查等。
一、算术运算符
用于执行基本的数学运算。
| 运算符 | 操作 | 示例 | 结果 |
|---|---|---|---|
+ | 加法 | 5 + 3 | 8 |
- | 减法 | 10 - 4 | 6 |
* | 乘法 | 7 * 2 | 14 |
/ | 除法 | 10 / 3 | 3(整数)或 3.333(浮点) |
% | 取余 | 10 % 3 | 1 |
csharp
int a = 10, 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(余数)
// 浮点除法——保留小数
double x = 10, y = 3;
Console.WriteLine(x / y); // 3.33333333333333
// 注意:整数除法截断
Console.WriteLine(10 / 3); // 3
Console.WriteLine(10 / 3.0); // 3.333...(除数是浮点,结果是浮点)
// 取余的常见用途:判断奇偶
bool isEven = (number % 2 == 0);算术溢出
csharp
int max = int.MaxValue; // 2,147,483,647
int overflowed = max + 1; // -2,147,483,648(默认不检查,绕回)
// 使用 checked 检查溢出
checked
{
// int result = max + 1; // 抛出 OverflowException
}二、自增和自减运算符
| 运算符 | 名称 | 行为 | 示例 |
|---|---|---|---|
++a | 前置自增 | 先加 1,再使用新值 | int b = ++a; → a=6, b=6 |
a++ | 后置自增 | 先使用当前值,再加 1 | int b = a++; → b=5, a=6 |
--a | 前置自减 | 先减 1,再使用新值 | int b = --a; → a=4, b=4 |
a-- | 后置自减 | 先使用当前值,再减 1 | int b = a--; → b=5, a=4 |
csharp
int a = 5;
Console.WriteLine(++a); // 6(先加再输出)
Console.WriteLine(a); // 6
int b = 5;
Console.WriteLine(b++); // 5(先输出再加)
Console.WriteLine(b); // 6三、赋值运算符
| 运算符 | 示例 | 等价于 |
|---|---|---|
= | a = 10 | 赋值 |
+= | a += 5 | a = a + 5 |
-= | a -= 3 | a = a - 3 |
*= | a *= 2 | a = a * 2 |
/= | a /= 2 | a = a / 2 |
%= | a %= 3 | a = a % 3 |
??= | a ??= b | a = a ?? b |
csharp
int x = 10;
x += 5; // x = 15
x *= 2; // x = 30
x %= 7; // x = 2 (30 % 7 = 2)四、比较运算符
返回 bool 值(true 或 false)。
| 运算符 | 操作 | 示例 | 结果 |
|---|---|---|---|
== | 等于 | 5 == 5 | true |
!= | 不等于 | 5 != 3 | true |
> | 大于 | 10 > 3 | true |
< | 小于 | 2 < 5 | true |
>= | 大于等于 | 4 >= 4 | true |
<= | 小于等于 | 3 <= 4 | true |
csharp
// 值类型的比较:比较值
int a = 10, b = 20;
bool equal = (a == b); // false
bool greater = (a > b); // false
bool lessOrEqual = (a <= b); // true
// 引用类型的比较:默认比较引用
string s1 = "Hello";
string s2 = "Hello";
Console.WriteLine(s1 == s2); // true(string 重载了 ==,比较值)
// 引用类型比较引用
object o1 = new object();
object o2 = new object();
Console.WriteLine(o1 == o2); // false(不同对象,不同引用)
Console.WriteLine(o1.Equals(o2)); // false(默认比较引用)五、逻辑运算符
| 运算符 | 操作 | 说明 | 示例 |
|---|---|---|---|
&& | 逻辑与(AND) | 全部为 true 才为 true | a > 0 && b > 0 |
| ` | ` | 逻辑或(OR) | |
! | 逻辑非(NOT) | 取反 | !(a > 0) |
csharp
// 短路求值
bool result1 = (10 > 0) && (10 / 0 == 1); // ❌ 不会执行到除法
// && 左侧为 false 时,右侧不执行
bool CheckAnd() => true && (true || (0 > 1));
// || 左侧为 true 时,右侧不执行
bool CheckOr() => false && (true || (0 > 1));
// 实际应用:安全检查
if (person != null && person.Age > 18) // ✅ 先检查 null,避免 NullReferenceException
{
Console.WriteLine("已成年");
}逻辑运算符 vs 位运算符
| 逻辑运算符 | 位运算符 | 区别 |
|---|---|---|
&& | & | && 短路求值,& 两侧都计算 |
| ` | ` |
六、位运算符
对整数的二进制位进行操作。
| 运算符 | 操作 | 示例 | 说明 |
|---|---|---|---|
& | 按位与 | 5 & 3 → 1 (0101 & 0011 = 0001) | 都为 1 才为 1 |
| ` | ` | 按位或 | `5 |
^ | 按位异或 | 5 ^ 3 → 6 (0101 ^ 0011 = 0110) | 不同为 1 |
~ | 按位取反 | ~5 → -6 (~0101 = 1010) | 1 变 0,0 变 1 |
<< | 左移 | 5 << 2 → 20 (0101 << 2 = 10100) | 相当于乘以 4 |
>> | 右移 | 5 >> 2 → 1 (0101 >> 2 = 0001) | 相当于除以 4 |
csharp
int a = 5; // 二进制:0101
int b = 3; // 二进制:0011
Console.WriteLine(a & b); // 1 (0001)
Console.WriteLine(a | b); // 7 (0111)
Console.WriteLine(a ^ b); // 6 (0110)
Console.WriteLine(~a); // -6 (1010)
Console.WriteLine(a << 2); // 20 (左移 2 位 = 乘以 4)
Console.WriteLine(a >> 1); // 2 (右移 1 位 = 除以 2)
// 位运算的常见用途
// 1. 使用 Flags 枚举(详见 04-常用数据类型)
// 2. 权限检查
bool hasReadPermission = (permission & Permission.Read) != 0;
// 3. 高效乘除 2 的幂
int doubled = value << 1; // 乘以 2
int halved = value >> 1; // 除以 2七、类型测试运算符
| 运算符 | 说明 | 示例 |
|---|---|---|
is | 检查类型是否匹配 | obj is string |
as | 安全转换为目标类型 | obj as string |
typeof | 获取类型的 Type 对象 | typeof(string) |
sizeof | 获取值类型的大小(字节) | sizeof(int) |
csharp
// is:类型检查(C# 7.0+ 支持模式匹配)
object obj = "Hello";
if (obj is string s) // 检查并转换
Console.WriteLine(s.Length);
// as:安全转换(失败返回 null,不抛异常)
string? text = obj as string;
if (text != null)
Console.WriteLine(text.Length);
// typeof:获取类型信息
Type type = typeof(string);
Console.WriteLine(type.FullName); // System.String
// sizeof:获取值类型大小(需在 unsafe 上下文中,或 .NET 6+ 直接使用)
Console.WriteLine(sizeof(int)); // 4
Console.WriteLine(sizeof(double)); // 8
Console.WriteLine(sizeof(decimal)); // 16八、其他运算符
1. 三元运算符 ? :
csharp
int a = 10, b = 20;
int max = a > b ? a : b; // 返回较大的值
Console.WriteLine(max); // 202. 空合并运算符 ?? 和 ??=
csharp
string? name = null;
string display = name ?? "默认值"; // name 为 null 时使用默认值
// ??=:左边为 null 时才赋值
name ??= "新值"; // name 为 null,赋值为 "新值"3. 空条件运算符 ?. 和 ?[]
csharp
Person? person = null;
string? city = person?.Address?.City; // 安全访问链
int[]? arr = null;
int? first = arr?[0]; // null,不抛异常4. nameof 运算符
获取变量、类型或成员的名称字符串,编译时确定。
csharp
string name = nameof(person); // "person"
string typeName = nameof(Person); // "Person"
string methodName = nameof(ToString); // "ToString"
string paramName = nameof(ArgumentException.ParamName); // "ParamName"
// 典型用途:异常参数名称
void ValidateAge(int age)
{
if (age < 0)
throw new ArgumentOutOfRangeException(nameof(age), "年龄不能为负数");
}
// 属性变更通知
public string Name
{
get => _name;
set
{
_name = value;
OnPropertyChanged(nameof(Name)); // 重命名属性时,这里会自动更新
}
}5. =>(Lambda 运算符)
csharp
// Lambda 表达式
Func<int, int> square = x => x * x;
// 表达式体方法(C# 6.0+)
int Add(int a, int b) => a + b;
// 表达式体属性
string FullName => $"{FirstName} {LastName}";
// 表达式体构造函数
public Person(string name) => Name = name;6. checked / unchecked
csharp
checked { int result = int.MaxValue + 1; } // 抛出异常
unchecked { int result = int.MaxValue + 1; } // 允许溢出(默认)7. await 运算符
csharp
async Task<int> GetDataAsync()
{
// await 等待异步操作完成
string result = await httpClient.GetStringAsync(url);
return result.Length;
}九、运算符优先级
优先级决定了表达式中运算的执行顺序。优先级高的先执行。
| 优先级 | 类别 | 运算符 |
|---|---|---|
| 最高 | 基本 | () [] ?. ?[] ++ -- new typeof sizeof nameof checked unchecked await |
| 1 | 一元 | + - ! ~ ++x --x (T) |
| 2 | 乘法 | * / % |
| 3 | 加法 | + - |
| 4 | 移位 | << >> |
| 5 | 关系和类型 | < > <= >= is as |
| 6 | 相等 | == != |
| 7 | 位与 | & |
| 8 | 位异或 | ^ |
| 9 | 位或 | ` |
| 10 | 逻辑与 | && |
| 11 | 逻辑或 | ` |
| 12 | 空合并 | ?? |
| 13 | 三元 | ?: |
| 14 | 赋值和 Lambda | = += -= *= /= %= ??= => |
csharp
int result = 5 + 3 * 2; // 11(乘法优先)
int result = (5 + 3) * 2; // 16(括号改变优先级)
// 复杂表达式建议加括号,提高可读性
int value = (a > b ? a : b) * 2; // 比不加括号更清晰十、运算符重载
允许自定义类型参与运算。
csharp
public struct Vector2
{
public double X { get; set; }
public double Y { get; set; }
// 重载 + 运算符
public static Vector2 operator +(Vector2 a, Vector2 b)
=> new Vector2 { X = a.X + b.X, Y = a.Y + b.Y };
// 重载 * 运算符(向量 × 标量)
public static Vector2 operator *(Vector2 v, double scalar)
=> new Vector2 { X = v.X * scalar, Y = v.Y * scalar };
// 重载 == 和 !=(通常需要同时重写 Equals 和 GetHashCode)
public static bool operator ==(Vector2 a, Vector2 b)
=> a.X == b.X && a.Y == b.Y;
public static bool operator !=(Vector2 a, Vector2 b)
=> !(a == b);
public override bool Equals(object? obj)
=> obj is Vector2 v && v == this;
public override int GetHashCode()
=> HashCode.Combine(X, Y);
public override string ToString() => $"({X}, {Y})";
}
// 使用
var v1 = new Vector2 { X = 1, Y = 2 };
var v2 = new Vector2 { X = 3, Y = 4 };
var sum = v1 + v2; // (4, 6)
var scaled = v1 * 2.5; // (2.5, 5)
Console.WriteLine(sum); // (4, 6)可重载的运算符
| 类别 | 可重载的运算符 |
|---|---|
| 算术 | + - * / % |
| 自增自减 | ++ -- |
| 比较 | == != < > <= >= |
| 位运算 | & ` |
| 不能重载 | = && ` |
重载运算符必须是
public static方法。
核心知识点总结
运算符分类速查
| 类别 | 运算符 |
|---|---|
| 算术 | + - * / % |
| 自增自减 | ++ -- |
| 赋值 | = += -= *= /= %= ??= |
| 比较 | == != < > <= >= |
| 逻辑 | && ` |
| 位运算 | & ` |
| 类型 | is as typeof sizeof |
| 空值 | ?? ?. ?[] |
| 其他 | ?: => nameof checked unchecked await new |
注意事项
- C# 中
=是赋值,==是相等判断——不要混淆 - 整数除法截断小数——
10 / 3= 3,不是 3.333 - 短路求值——
&&和||左侧满足条件时右侧不执行 - 优先使用括号——不要依赖优先级记忆,加括号更清晰
is模式匹配——比is+ 强制转换更安全、更简洁nameof编译时确定——重构时自动更新,优于字符串字面量sizeof只能用于值类型——引用类型大小不由开发者直接控制- 运算符重载要符合直觉——
+应该表示相加,不要用于减法等


