In this article I am going to describe some confusing terms including Func, Action, Predicate, lambda expression and delegate and their differences. Although the introduction of generics and lambda expressions added a bit to the complexity of these terms, they are all the same, Delegates.
This helps you to have a better understanding of these terms and could possibly solve complex interactions between objects and collections.
Delegate has existed in the programming world since a long time ago, however throughout the time many forms of delegates have been added to .Net for the purpose of creating more ways to solve problems.
Let's start with a simple definition of delegates. A delegate is a reference to a piece of code that does something!, exactly like a method. It has arguments as input and results as output.
Hide Copy Code
delegate(int x) { return x == 0; }
If we replace the delegate keyword with
Hide Copy Code
public bool FunctionName
then it would become
Hide Copy Code
public bool FunctionName(int x){ return x == 0; }
so basically in one form it could be a function without a name and more technically an anonymous function. A delegate can also have a name like below and it refers to a method with the same signature.
Hide Copy Code
public bool delegate Name(int x);
and then this could be associated with an event so that other objects can subscribe to it and be notified whenever the event is fired (which is out of scope for this article).
Now that we know what a delegate is, let's use it somewhere exciting such as LINQ and collections. Say we have a collection like this:
Hide Copy Code
var collection = new List<int>(); collection.Add(1); collection.Add(2); collection.Add(3);
If we want to filter the elements of this collection, we normally write something like:
Hide Copy Code
collection.Where(x => x == 2);
I will get back to lambda expressions and explain them as part of this article.
Delegate
Now what if I want to describe the condition inside parenthesis with a delegate. Remember a delegate is supposed to reference to a piece of code that does something so basically like a method. So why not comparing some elements and filter them for us, like the lambda expression that I talked about. So what if I write something like:
Hide Copy Code
delegate(int item) { if(item == 2) { return true; } return false; }
and subsequently:
Hide Copy Code
collection.Where(delegate(int item) { if (item == 2) { return true; } return false; });
How was that? So simply this was a delegate which was taking an integer as an input and was returning a boolean as output.
Func
Another form of a delegate is Func. Func could have any number and type of input arguments and could return any type of output, exactly like a delegate. The signature is something like:
Hide Copy Code
Func<int, bool> condition = delegate(int item) { if(item == 2) { return true; } return false; };
Because Func could return any type, we need to mention the return type in the signature (in this case the bool, which is literrally the last argument in the generic definition.
Hide Copy Code
Func<int, bool> condition = deleg...
And I can write:
Hide Copy Code
var result = collection.Where(condition);
Predicate
If a Func only returns a bool, that would have another name called a Predicate, just to confuse us a bit more. So a predicate is a delegate that always returns a boolean with any number and type of inputs. Therefore:
Hide Copy Code
Predicate<int> predicate = delegate(int item) { if(item == 2) { return true; } return false; };
However we need to convert this predicate to a Func if we want to use it like before:
Hide Copy Code
Func<int, bool> condition = new Func<int, bool>(predicate); collection.Where(condition);
But there are situations that we could use a predicate directly:
Hide Copy Code
collection.Find(predicate);
Best Practice: Use Func, Lambda Expressions and Delegates instead of Predicates.
Action
Now what if my Func didn't return any value, what would be that called then? Yes! an Action. So basically an Action is a delegate that does something without any return value. Something like:
Hide Copy Code
Action<int> action = delegate(int item) { item += 2; }
Can I use this action with the previous example of filtering? Not really, because the argument to the Where method should return a boolean to evaluate whether that item should be included in the result or not, right? So Where can I use an Action?
The answer is whenever I wanted to do an operation without any return value. like:
Hide Copy Code
collection.ForEach(action);
So this will run for every item in the collection and adds 2 to each item. However note that although this action is run for each item in the collection, in this case the change is not applied because we are modifying a collection using a LINQ for each loop which is not allowed. But it was a Reference type, properties of that type could be changed in a for each loop, like the samples below.
Lambda Expression
Now I'd like to define another synonym for a delegate, a Lambda Expression. A lambda expression can be in the form of a Func or an Action, and therefor inherently it's a delegate.
So the replacement for the Where argument could be:
Hide Copy Code
Predicate<int> condition = (int x) => x == 2; Func<int, bool> condition = (int x) => x == 2;
and for the ForEach method I can write:
Hide Copy Code
Action<int> action = (int item) => item += 2;
And that's it. We are there, with all the confusion gone. I have written some samples below for each of these terms in a more complicated fashion. Hope it helps.
Samples
Hide Copy Code
public class Customer { public string Name { get; set; } public string Telephone { get; set; } }
Hide Copy Code
var customers = new List<Customers>(); customers.Add(new Customer() { Name = "cust A", Telephone = "123"});
Hide Copy Code
customers.Add(new Customer() { Name = "cust B", Telephone = "456"});
Hide Copy Code
customers.Add(new Customer() { Name = "cust C", Telephone = "789"});
1. Delegate
Hide Copy Code
customers.Where(delegate(Customer cust) { return cust.Name == "cust B"; });
2. Func
Hide Copy Code
Func<Customer, bool> filter = delegate(Customer cust){ return cust.Name == "cust B"; }; customers.Where(filter);
3. Predicate
Hide Copy Code
Predicate<Customer> filter = delegate(Customer cust){ return cust.Name == "cust B"; }; customers.Find(filter);
4. Lambda
Hide Copy Code
Func<Customer, bool> filter = (Customer cust) => cust.Name == "cust B"; customers.Where(filter);
5. Action
Hide Copy Code
We can't use Actions here because it doesn't return any value let alone bool.
Now lets play with ForEach method, instead of filtering data:
1. Delegate
Hide Copy Code
customers.ForEach(delegate(Customer cust){ cust.Name = "cust B name Modified !!!"; });
2. Func
Hide Copy Code
We can't use Func here, as Func has to always return something.
3. Predicate
Hide Copy Code
We can't use predicate here as well, as it always has to return boolean.
4. Lambda
Hide Copy Code
customers.ForEach(cust => cust.Name = "cust B Modified !!!");
5. Action
Hide Copy Code
Action<Customer> filter = (Customer cust) => cust.Name = "cust B Modified !!!"; customers.ForEach(filter);
*) Now a key question! why the following code snippet has a compilation error:
Hide Copy Code
var simpleDelegate = delegate(int x) { return x == 0; };