在.NET开发中,我们经常遇到需要根据不同条件执行不同逻辑的场景。很多开发者习惯性地使用if-else链来处理这类问题,但随着条件分支的增加,代码会变得臃肿难读。实际上,C#中的switch语句经过多次版本迭代,已经发展出许多强大的特性,能够显著提升代码的可读性和性能。
本文将分享5个实战中高频使用的switch高级技巧,以及常见的陷阱规避方法。这些技巧特别适合.NET 6/8项目,能帮助你写出更优雅、更高效的代码。无论你是正在重构旧代码,还是开发新功能,这些经验都能立即提升你的编码水平。
C# 7.0引入的模式匹配功能彻底改变了switch语句的用法。现在,我们可以基于类型和条件进行更灵活的匹配,而不仅仅是简单的值比较。
csharp复制object obj = GetSomeObject();
switch (obj)
{
case int i when i > 100:
Console.WriteLine($"大整数: {i}");
break;
case int i:
Console.WriteLine($"普通整数: {i}");
break;
case string s when s.Length > 5:
Console.WriteLine($"长字符串: {s}");
break;
case string s:
Console.WriteLine($"普通字符串: {s}");
break;
case null:
Console.WriteLine("对象为null");
break;
default:
Console.WriteLine("未知类型");
break;
}
这种模式匹配特别适合处理多态对象或未知类型的输入。相比传统的if-else链,它有三大优势:
提示:在.NET 6/8中,模式匹配的性能进一步优化,是替代is和as操作符的理想选择。
当需要同时判断多个条件时,传统的做法是嵌套if语句或使用复杂的逻辑运算符。C# 7.0引入的元组模式让这类场景变得异常简洁。
csharp复制var point = (x: 10, y: 20, color: "red");
switch (point)
{
case ( > 0, > 0, "red"):
Console.WriteLine("第一象限的红色点");
break;
case ( < 0, > 0, var color) when color != "blue":
Console.WriteLine("第二象限的非蓝色点");
break;
case (0, 0, _):
Console.WriteLine("坐标原点");
break;
default:
Console.WriteLine("其他情况");
break;
}
元组匹配特别适合处理:
C# 8.0引入了switch表达式,进一步简化了返回值场景下的代码。这种形式去掉了break语句,使用=>符号直接返回值,使代码更加紧凑。
csharp复制var score = 85;
var grade = score switch
{
>= 90 => "A",
>= 80 => "B",
>= 70 => "C",
>= 60 => "D",
_ => "F" // 默认情况
};
Console.WriteLine($"成绩等级: {grade}");
表达式形式的优势对比:
| 传统switch语句 | switch表达式 |
|---|---|
| 需要break语句 | 无需break |
| 多行代码块 | 单行表达式 |
| 适合复杂逻辑 | 适合简单返回值 |
| 需要显式return | 直接返回值 |
将when子句、属性模式等特性组合使用,可以解决更复杂的业务场景。下面是一个电商订单处理的例子:
csharp复制var order = new Order
{
Status = OrderStatus.Processing,
Items = new List<Item> { /*...*/ },
Customer = new Customer { Level = "VIP" }
};
var discount = order switch
{
{ Status: OrderStatus.Cancelled } => 0m,
{ Customer.Level: "VIP" } when order.Total > 1000 => 0.15m,
{ Items.Count: > 5 } => 0.1m,
{ Created: var date } when date.Date == DateTime.Today => 0.05m,
_ => 0m
};
这个例子展示了如何组合使用:
即使是有经验的开发者,也容易掉入switch语句的一些陷阱。以下是几个需要特别注意的地方:
case穿透问题:
csharp复制switch (value)
{
case 1:
Console.WriteLine("Case 1");
// 忘记break会导致执行下一个case!
case 2:
Console.WriteLine("Case 2");
break;
}
注意:除非有意设计,否则每个case都应该以break、return或throw结束。
null处理:
csharp复制string input = GetPossibleNull();
// 传统方式需要显式检查null
switch (input)
{
case null:
// 处理null
break;
case "value":
// 处理特定值
break;
}
// C# 8.0+可以用更简洁的方式
var result = input switch
{
null => "空值",
"value" => "特定值",
_ => "其他"
};
性能优化技巧:
可维护性建议:
让我们看一个实际的重构例子。假设我们有一个处理各种通知消息的方法:
csharp复制// 重构前
void ProcessNotification(Notification notification)
{
if (notification == null)
{
throw new ArgumentNullException();
}
if (notification.Type == "Email")
{
if (notification.Priority == "High")
{
SendImmediateEmail(notification);
}
else
{
SendRegularEmail(notification);
}
}
else if (notification.Type == "SMS")
{
if (notification.IsInternational)
{
SendInternationalSms(notification);
}
else
{
SendLocalSms(notification);
}
}
else if (notification.Type == "Push")
{
SendPushNotification(notification);
}
else
{
LogUnknownNotificationType(notification.Type);
}
}
// 重构后
void ProcessNotification(Notification notification)
{
switch (notification)
{
case null:
throw new ArgumentNullException();
case { Type: "Email", Priority: "High" }:
SendImmediateEmail(notification);
break;
case { Type: "Email" }:
SendRegularEmail(notification);
break;
case { Type: "SMS", IsInternational: true }:
SendInternationalSms(notification);
break;
case { Type: "SMS" }:
SendLocalSms(notification);
break;
case { Type: "Push" }:
SendPushNotification(notification);
break;
default:
LogUnknownNotificationType(notification.Type);
break;
}
}
重构后的版本有显著改进:
在Razor页面开发中,这些技巧同样适用。例如处理表单提交:
csharp复制@functions {
string HandleFormAction(string action)
{
return action switch
{
"save" => SaveData(),
"delete" => DeleteData(),
"preview" => PreviewData(),
_ => throw new InvalidOperationException($"未知操作: {action}")
};
}
}
掌握这些switch的高级用法后,你会发现许多复杂的条件逻辑都能以更优雅的方式实现。特别是在.NET 6/8中,这些特性已经非常成熟,性能优化到位,完全可以放心使用。