Predicate in c# and Its use case

Predicate delegate takes only one argument and returns a boolean value. 




Example:
static void Main()
{
    // return true if passed argument is greater than 4
    Predicate<int> greaterThanFour = (n) => n>4;

    Console.WriteLine(greaterThanFour(4)); //false
}

It is the very basic example of predicate, which returns true if number is greater than four otherwise it returns false.

You already have used the predicate if you have used the LINQ.

List<Order> orders = new List<Order>
{
    new Order { OrderId = 101, Status = "Pending" },
    new Order { OrderId = 102, Status = "Completed" },
    new Order { OrderId = 103, Status = "Cancelled" },
    new Order { OrderId = 104, Status = "Completed" }
};

// Find order with id 104
var order = orders.Find(o => o.OrderId == 104);

The find method takes the predicate as the parameter. Which returns true if it finds the order with id 104. Which is same as the below

Predicate<Order> findOrder = order => order.OrderId == 104;
 var order = orders.Find(findOrder);

Use case of predicate

public class Order
{
    public decimal TotalAmount { get; set; }
    public string Category { get; set; } = string.Empty;
    public bool IsVIP { get; set; }
    public bool IsHolidaySeason { get; set; }
}
public class DiscountService
{
    public static decimal ApplyDiscount(Order order)
    {
        decimal finalAmount = order.TotalAmount;

        if (order.IsHolidaySeason && order.TotalAmount > 100)
        {
            finalAmount*= 0.9m; // 10% discount
        }
        if (order.IsVIP)
        {
            finalAmount *= 0.8m; // 20% discount for VIPs
        }
        if (order.Category == "Electronics")
        {
            finalAmount *= 0.85m; // 15% discount on electronics
        }
        return finalAmount; // No discount
    }

    public static void Main()
    {
        Order order = new Order
                      {
                       TotalAmount = 150,
                       IsHolidaySeason = true,
                       IsVIP = false,
                       Category = "Electronics"
                      };

        decimal finalAmount = ApplyDiscount(order);

        Console.WriteLine($"Final amount after discount: {finalAmount}");
    }
}

In this example, we have a DiscountService which have a ApplyDiscount method that returns the finalAmount on the basis of various discounts. Discounts are not fix and changes frequently. In future, you can change the holiday discount, or you don't want to give any holiday discount, or you might introduce the other discount types. For that, you have to change the the ApplyDiscount method each time and test this method. That might be problematic in the production code (You might disagree with this and cover it with unit testing).

Let's try to utilize it with the predicate.

public class Order
{
    public decimal TotalAmount { get; set; }
    public string Category { get; set; } = string.Empty;
    public bool IsVIP { get; set; }
    public bool IsHolidaySeason { get; set; }
    public bool IsClearanceSale { get; set; }
}

public class DiscountServiceWithPredicate
{
    private static List<(Predicate<Order>, decimal)> discounts = new()
    {
        (ord => ord.IsHolidaySeason && ord.TotalAmount > 100, 0.10m),  // Holiday discount
        (ord => ord.IsVIP, 0.20m),  // VIP discount
        (ord => ord.Category == "Electronics", 0.15m),  // Electronics discount
        (ord => ord.IsClearanceSale, 0.50m)  // Clearance sale discount
    };
    
public static void Main()
    {
        Order order = new Order { TotalAmount = 150, IsHolidaySeason = true, IsVIP = false, Category = "Electronics", IsClearanceSale = false };

        decimal finalAmount = ApplyDiscounts(order);

        Console.WriteLine($"Final amount after discount: {finalAmount}");
    }

    private static decimal ApplyDiscounts(Order order)
    {
        decimal amount = order.TotalAmount;

        foreach(var (predicate, discountPercentage) in discounts)
        {
            if(predicate(order))
            {
                amount *= (1 - discountPercentage);
            }
        }
        return amount;
    }
}

In this code section, we easily update the discount value, add the new discount type or remove the existing discount type, without changing the ApplyDiscounts method.

Connect with me

👉 YouTube: https://youtube.com/@ravindradevrani

👉 Twitter: https://twitter.com/ravi_devrani

👉 GitHub: https://github.com/rd003

Comments

Popular posts from this blog

Asp.net core JWT authentication and role-based authorization (.NET 8.0)

Registering Multiple Implementation With Same Interface