Meta Description:
Learn how to use logical and relational patterns in C# through a real-world logistics example. Discover how patterns like not
, and
, or
, and relational comparisons simplify complex order processing, making code more readable and aligned with business requirements.
In an e-commerce logistics system, we often need to process orders with various statuses, priorities, and special handling requirements. C#'s logical and relational patterns help simplify these complex checks by allowing us to use not
, and
, or
, and relational comparisons directly in conditions. This article will walk you through using these patterns in a logistics scenario, complete with full code examples.
Setting Up Our Classes
For this example, we’ll use an Order
class with properties that represent logistics details, such as Status
, IsExpress
, and TotalAmount
. We’ll also define two specialized subclasses—ExpressOrder
for orders needing expedited processing and CancelledOrder
for tracking canceled orders.
public class Order
{
public int Status { get; set; }
public bool IsExpress { get; set; }
public decimal TotalAmount { get; set; }
}
public class ExpressOrder : Order
{
public DateTime ExpeditedProcessingDate { get; set; }
public bool RequiresSignature { get; set; }
public void ScheduleExpeditedProcessing()
{
ExpeditedProcessingDate = DateTime.Now.AddHours(2); // Process within 2 hours
Console.WriteLine($"Express order scheduled for expedited processing at {ExpeditedProcessingDate}");
}
public override string ToString()
{
return $"ExpressOrder [TotalAmount: {TotalAmount}, Status: {Status}, " +
$"IsReady: {IsExpress}, RequiresSignature: {RequiresSignature}]";
}
}
public class CancelledOrder : Order
{
public DateTime CancelledDate { get; set; }
public string CancellationReason { get; set; }
public void RecordCancellation(string reason)
{
CancelledDate = DateTime.Now;
CancellationReason = reason;
Console.WriteLine($"Order cancelled on {CancelledDate}: {CancellationReason}");
}
public override string ToString()
{
return $"CancelledOrder [TotalAmount: {TotalAmount}, Status: {Status}, " +
$"IsReady: {IsExpress}, CancelledDate: {CancelledDate}, Reason: {CancellationReason}]";
}
}
With these classes, we can now explore how to use logical and relational patterns to handle various order scenarios.
1. The not
Pattern: Filtering Out Cancelled Orders
When processing active orders, it’s essential to exclude any that are cancelled. Using the not
pattern, we can quickly check if an order isn’t a CancelledOrder
type:
Order order = new Order { Status = 50, IsExpress = true, TotalAmount = 250.00M };
if (order is not CancelledOrder)
{
Console.WriteLine("Order is active and can proceed.");
}
In this example, only non-cancelled orders are processed.
2. The and
Pattern: Processing Express Orders in a Specific Range
For express orders, we might prioritize orders based on TotalAmount
. Let’s say orders between $100 and $500 should be expedited:
if (order is { IsExpress: true, TotalAmount: > 100M and <= 500M })
{
Console.WriteLine("This express order qualifies for urgent processing.");
}
With and
, both conditions (express shipping and specific amount range) must be true to match, helping to prioritize orders easily.
3. The or
Pattern: Flexible Handling for Special Orders
If we want to handle any ExpressOrder
or orders with a TotalAmount
over $1000 as priority, the or
pattern provides the flexibility we need:
if (order is ExpressOrder or { TotalAmount: > 1000M })
{
Console.WriteLine("This order is a priority (either express or high-value).");
}
This pattern matches either condition, allowing us to identify high-priority orders efficiently.
4. Relational Patterns: Setting Specific Amount Thresholds
Relational patterns allow for comparing properties against constant values. For instance, if we consider any order over $750 to be “high-value”:
if (order.TotalAmount is > 750M)
{
Console.WriteLine("This is a high-value order.");
}
This pattern makes threshold-based checks clear and straightforward.
5. Combining Patterns with and
for Express Orders with Medium Value
Suppose express orders between $200 and $500 qualify for fast-track handling. We can use and
to set both conditions in a single check:
if (order is { IsExpress: true, TotalAmount: > 200M and <= 500M })
{
Console.WriteLine("This express order qualifies for fast-track handling.");
}
This approach provides precise filtering for orders based on multiple properties.
6. Excluding Specific Values with not
To exclude specific values within a range, the not
pattern can help. For instance, if TotalAmount
should be between $200 and $500 but not exactly $400:
if (order is { TotalAmount: > 200M and <= 500M and not 400M })
{
Console.WriteLine("Order is eligible for processing, excluding those with a total of $400.");
}
This approach allows for refined processing by excluding exact values.
7. Combining Type Patterns and Logical Patterns for Complex Conditions
We can use type patterns with and
and or
to manage express orders more effectively. Let’s say express orders above $1000 are “VIP” orders, while others are regular express orders:
if (order is ExpressOrder { TotalAmount: > 1000M })
{
Console.WriteLine("This is a VIP express order.");
}
else if (order is ExpressOrder)
{
Console.WriteLine("This is a standard express order.");
}
With this pattern, you can differentiate express orders based on TotalAmount
.
Full Code Example: Processing Orders with Logical and Relational Patterns
Here’s a complete code example that demonstrates handling different order types in our logistics system:
public static void ProcessOrder(Order order)
{
switch (order)
{
case CancelledOrder cancelled:
Console.WriteLine($"Order is cancelled. Reason: {cancelled.CancellationReason}");
break;
case ExpressOrder { TotalAmount: > 1000M }:
Console.WriteLine("This is a VIP express order.");
break;
case ExpressOrder { TotalAmount: > 200M and <= 500M }:
Console.WriteLine("Express order qualifies for fast-track handling.");
break;
case { TotalAmount: > 750M }:
Console.WriteLine("High-value order for special handling.");
break;
case Order { IsExpress: true, TotalAmount: > 100M and <= 500M }:
Console.WriteLine("Express order qualifies for urgent processing.");
break;
case Order { TotalAmount: > 200M and <= 500M and not 400M }:
Console.WriteLine("Order is eligible for processing, excluding total of $400.");
break;
default:
Console.WriteLine("Standard processing for order.");
break;
}
}
// Example Usage
Order regularOrder = new Order { Status = 50, IsExpress = false, TotalAmount = 300M };
ExpressOrder vipExpressOrder = new ExpressOrder { Status = 50, IsExpress = true, TotalAmount = 1200M };
CancelledOrder cancelledOrder = new CancelledOrder { Status = 0, IsExpress = false, TotalAmount = 0M, CancellationReason = "Out of stock" };
ProcessOrder(regularOrder); // Output: Standard processing for order.
ProcessOrder(vipExpressOrder); // Output: This is a VIP express order.
ProcessOrder(cancelledOrder); // Output: Order is cancelled. Reason: Out of stock
Summary
Logical and relational patterns in C# provide clear and concise ways to handle complex business logic. Here’s a recap:
-
not
: Filters out specific conditions, like excluding cancelled orders. -
and
: Allows precise conditions, like checking if an order is express and within a price range. -
or
: Matches one of multiple conditions, helpful for flexible priority checks. - Relational Patterns: Simplifies comparisons against constant values, useful for thresholds.
These patterns help make your code more readable and maintainable while aligning with business rules. Try using these patterns to see how they can simplify your logic and improve your application!
Top comments (0)