Delegates (Delegate, Func, Predicate, Action and Lambda)

  

Introduction

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.

Background

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;
};


你可能感兴趣的:(DESCRIBE,expression,including,possibly,complexity)