在.NET开发中,我们经常需要处理各种状态流转和条件分支。虽然现代编程语言提供了多种控制流结构,但switch case语句因其清晰直观的特点,依然是处理枚举型业务逻辑的首选工具。本文将带你从基础的分数评级案例出发,逐步深入到电商订单状态机等真实业务场景,展示如何用C#的switch case构建健壮的业务逻辑。
当我们处理像订单状态、用户权限或工作流步骤这类具有明确离散状态的业务逻辑时,switch case提供了一种结构化的处理方式。与if-else链相比,它更易于阅读和维护,特别是当状态数量较多时。
考虑一个简单的电商订单状态枚举:
csharp复制public enum OrderStatus
{
PendingPayment, // 待支付
Paid, // 已支付
Shipping, // 发货中
Completed, // 已完成
Cancelled // 已取消
}
使用switch case处理这些状态转换,代码会非常清晰:
csharp复制public void ProcessOrder(OrderStatus status)
{
switch(status)
{
case OrderStatus.PendingPayment:
HandlePendingPayment();
break;
case OrderStatus.Paid:
HandlePaidOrder();
break;
// 其他状态处理...
}
}
在实际业务中,状态转换往往不是随意的。我们需要确保只有合法的状态转换才能发生。例如,订单从"已取消"状态不能直接变为"已完成"。
我们可以通过switch case实现这种约束:
csharp复制public bool TryChangeStatus(OrderStatus current, OrderStatus next)
{
switch(current)
{
case OrderStatus.PendingPayment:
return next == OrderStatus.Paid || next == OrderStatus.Cancelled;
case OrderStatus.Paid:
return next == OrderStatus.Shipping || next == OrderStatus.Cancelled;
case OrderStatus.Shipping:
return next == OrderStatus.Completed;
default:
return false; // 已完成或已取消的订单不能再改变状态
}
}
这种显式的状态转换规则定义,使得业务逻辑一目了然,也便于后续维护。
在ASP.NET Core的Razor Pages应用中,我们经常需要在页面处理器方法中处理不同的业务状态。switch case在这里尤其有用。
假设我们有一个订单详情页面,需要根据订单状态显示不同的操作按钮:
csharp复制public class OrderDetailsModel : PageModel
{
public IActionResult OnPostProcessOrder(string actionType)
{
var order = GetCurrentOrder();
switch(order.Status)
{
case OrderStatus.PendingPayment:
if(actionType == "cancel")
{
order.Status = OrderStatus.Cancelled;
// 取消订单逻辑...
}
break;
case OrderStatus.Paid:
if(actionType == "ship")
{
order.Status = OrderStatus.Shipping;
// 发货逻辑...
}
break;
// 其他状态处理...
}
_dbContext.SaveChanges();
return RedirectToPage();
}
}
虽然switch case在很多场景下非常有用,但当业务逻辑变得过于复杂时,可能需要考虑使用策略模式。策略模式通过将每种状态的处理逻辑封装到单独的类中,可以提供更好的扩展性和可测试性。
以下是一个简单的策略模式实现:
csharp复制public interface IOrderStatusHandler
{
bool CanHandle(OrderStatus status);
void Handle(Order order);
}
public class PaidOrderHandler : IOrderStatusHandler
{
public bool CanHandle(OrderStatus status) => status == OrderStatus.Paid;
public void Handle(Order order)
{
// 处理已支付订单的逻辑
}
}
// 使用时
var handlers = new List<IOrderStatusHandler>
{
new PaidOrderHandler(),
new ShippingOrderHandler(),
// 其他处理器...
};
var handler = handlers.FirstOrDefault(h => h.CanHandle(order.Status));
handler?.Handle(order);
那么,何时应该从switch case升级到策略模式呢?这里有几个判断标准:
让我们把这些概念整合起来,构建一个完整的订单状态机。我们将使用switch case处理状态转换,同时保持代码的清晰和可维护性。
首先,定义订单类:
csharp复制public class Order
{
public int Id { get; set; }
public OrderStatus Status { get; set; }
public DateTime StatusChangedDate { get; set; }
public void ChangeStatus(OrderStatus newStatus)
{
if(!IsValidTransition(newStatus))
throw new InvalidOperationException("Invalid status transition");
Status = newStatus;
StatusChangedDate = DateTime.UtcNow;
}
private bool IsValidTransition(OrderStatus newStatus)
{
switch(Status)
{
case OrderStatus.PendingPayment:
return newStatus == OrderStatus.Paid ||
newStatus == OrderStatus.Cancelled;
case OrderStatus.Paid:
return newStatus == OrderStatus.Shipping ||
newStatus == OrderStatus.Cancelled;
case OrderStatus.Shipping:
return newStatus == OrderStatus.Completed;
default:
return false;
}
}
}
然后,在服务层处理订单状态相关的业务逻辑:
csharp复制public class OrderService
{
public void ProcessOrder(int orderId)
{
var order = _dbContext.Orders.Find(orderId);
switch(order.Status)
{
case OrderStatus.PendingPayment:
if(IsPaymentReceived(order))
{
order.ChangeStatus(OrderStatus.Paid);
SendPaymentConfirmation(order);
}
break;
case OrderStatus.Paid:
if(IsReadyToShip(order))
{
order.ChangeStatus(OrderStatus.Shipping);
CreateShippingLabel(order);
}
break;
// 其他状态处理...
}
_dbContext.SaveChanges();
}
}
C# 8.0引入了switch表达式,可以进一步简化某些场景下的代码。例如,状态转换验证可以写成:
csharp复制private bool IsValidTransition(OrderStatus newStatus) => Status switch
{
OrderStatus.PendingPayment => newStatus == OrderStatus.Paid ||
newStatus == OrderStatus.Cancelled,
OrderStatus.Paid => newStatus == OrderStatus.Shipping ||
newStatus == OrderStatus.Cancelled,
OrderStatus.Shipping => newStatus == OrderStatus.Completed,
_ => false
};
同样,状态相关的消息也可以简洁地处理:
csharp复制public string GetStatusDescription(OrderStatus status) => status switch
{
OrderStatus.PendingPayment => "等待买家付款",
OrderStatus.Paid => "已付款,等待发货",
OrderStatus.Shipping => "商品已发货",
OrderStatus.Completed => "交易完成",
OrderStatus.Cancelled => "订单已取消",
_ => "未知状态"
};
在使用switch case处理业务逻辑时,良好的错误处理至关重要。特别是当处理枚举值时,总应该考虑默认情况:
csharp复制public void ProcessOrder(Order order)
{
switch(order.Status)
{
case OrderStatus.PendingPayment:
// ...
break;
// 其他已知状态处理...
default:
_logger.LogWarning($"未知或未处理的订单状态: {order.Status}");
throw new NotSupportedException($"订单状态{order.Status}不受支持");
}
}
另一个常见错误是忘记break语句,这会导致意外的"贯穿"行为。C#要求每个非空case块必须以break、return或throw等语句结束,这实际上是一个优点,因为它强制我们明确每个分支的结束。
测试switch case逻辑时,应该确保覆盖所有可能的分支。对于订单状态机,这意味着我们需要测试:
使用xUnit的示例测试:
csharp复制public class OrderStatusTests
{
[Theory]
[InlineData(OrderStatus.PendingPayment, OrderStatus.Paid, true)]
[InlineData(OrderStatus.PendingPayment, OrderStatus.Cancelled, true)]
[InlineData(OrderStatus.PendingPayment, OrderStatus.Completed, false)]
public void TestStatusTransitions(OrderStatus current, OrderStatus next, bool expected)
{
var order = new Order { Status = current };
Assert.Equal(expected, order.IsValidTransition(next));
}
}
对于更复杂的处理逻辑,可以考虑为每个状态编写单独的测试类,这样当添加新状态时,可以更容易地确保测试覆盖率。