Search code examples
c#predicate

When do I use a predicate


I understand what a predicate is and how to create one, but when do I use it? Why would I use it?

for example:

Predicate<string> isUpper = s => s.Equals(s.ToUpper());

Why would I do this or anything like it?


Solution

  • Firstly, I'll summarize delegates and lambda expressions.

    Delegates


    In C#, delegates allow you to create types which represent functions. In other words, delegates allow you to save a reference to a method in a variable. Once, we store a reference to that method, we can use this variable to call the method it is referring to. So you are indirectly calling the method. The term "delegate" in C# very well lives up to the literal meaning of delegate.

    To store a reference to a method, you first need to create a compatible delegate:

    delegate int MyDelegate(string parameter)
    

    The above statement creates a delegate that can hold a reference to any method that has a return type of int and takes a single string as parameter. For instance, I'm creating a trivial class and a method here for demo:

    class AClass 
    {
        public int StringLength(string str) { return str.Length; }
    }
    

    Once you create a delegate, you can use it like a type:

    AClass obj = new AClass()
    MyDelegate d = obj.StringLength
    

    Notice I have skipped the parentheses after StringLength. Now I can do this:

    int length = d("Hello");
    

    This will indirectly call, obj.StringLength() and the value it returns will be returned by d and stored in length.

    There's more to delegates like multicast delegates which are beyond the scope of this answer.

    Why are delegates powerful?

    They help you to easily pass around functions/methods just like objects. You can pass your own method to a class that knows nothing about your method but will call it later through the delegate. This allows you to embed your custom logic into code that you know nothing about.

    Lambda Expressions


    A lambda expression is nothing but a function literal. It helps to quickly create a function without creating a new class and a new method. In the previous example, I have created a class called AClass with a method StringLength. You can see that all the method body consists is of a single line. With lambdas, you can skip creating a class and a method and directly specify the method body:

    MyDelegate d = str => str.Length;
    

    The Predicate<T> Delegate


    Predicate<T> is just another delegate as well, defined as follows:

    delegate bool Predicate<T>(T obj)
    

    This is a delegate with a generic parameter. This allows you to pass it a parameter of any type which will be specified later when the delegate variable is created but the return type is always bool. A predicate is, in essence, just a condition, that can be either true or false.

    Coming to your question, with predicates, you can pass in such a condition to a method so that the method can use it to check if it holds true for something that is private to that class. You are basically passing in logic that will be executed later.

    A common use case of predicates is filtering. If I have a collection of elements, and I want to filter the elements and obtain only those elements for which a condition holds true, I can use a Predicate<T> parameter:

    
    public List<int> Filter(List<int> list, Predicate<int> filter)
    {
        public var filteredList = new List<int>();
        foreach (int element in list)
        {
            if (filter(element))
            {
                filteredList.Add(element); //Add to list only if condition holds true for the element
            }
            
        }
    }
    
    

    Now, suppose, I have a list and I want only the even numbers and numbers greater than 5, then I can simply do this:

    var numbers = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
    var evenNumbers = Filter(numbers, element => element % 2 == 0);    // 2, 4, 6, 8
    var numbersGreaterThan5 = Filter(numbers, element => element > 5); // 6, 7, 8, 9
    

    In practice, you will hardly have to create a custom Filter() method since System.Linq already has a method/LINQ operator called Where() that does this for you. The parameter to this Where() method is not a Predicate<T> but an identical(compatibility-wise) Func<T, bool> which is another delegate defined as follows:

    delegate TResult Func<in T, out TResult>(T arg)
    

    Func<T, bool> and Predicate<T> are therefore identical regards to the methods they can reference. Moreover, you can create your own delegate with the same signature as the predicate. The framework has a bunch of them defined to make life a bit easier for you.