Introduction
When LINQ appeared on our screens, it brought along a requirement under the guise of 'Func
' whenever you wanted to do anything substantial, such as supply the contents of a where
clause:
What exactly is Func? What are we actually being asked for here? This is a journey that begins with delegates.
First, let's consider how we create an object:
class Program
{
class Car
{
public Car(string make)
{
Console.WriteLine("{0} created", make);
}
}
static void Main(string[] args)
{
Car pointerToObject;
pointerToObject = new Car("Ford Fiesta");
Console.ReadLine();
}
As you can see, this takes three steps:
- We define a class (
Car
) - We create a variable of that type
- Then we create an instance of the class and set the variable to a reference of it
A bit simple? Stick with me...
Now let's see how we do this with delegates. I invite you to spot the difference:
class Program
{
delegate void Car(string make);
private static void ReturnCarName(string make)
{
Console.WriteLine("{0} created", make);
}
static void Main(string[] args)
{
Car pointerToMethod;
pointerToMethod = ReturnCarName;
pointerToMethod("Ford Fiesta");
Console.ReadLine();
}
}
In both instances, Car
acted as a pointer to some functionality, the first was a reference to an instance of a class whereas the second was reference to a method. You've spotted the difference.
- Class type = Reference to a object
- Delegate type = Reference to a method
Directly from the C# Spec:
1.11 Delegates
A delegate type represents references to methods with a particular parameter list and return type. Delegates make it possible to treat methods as entities that can be assigned to variables and passed as parameters.
That last sentence is extremely important. Delegates allow us to pass references to methods in the same way we can pass references to objects.
This is the purpose of Func
. Instead of being forced to define our own delegate (what we did in the second example, named Car
), we're provided with a definition of a delegate of which we have to provide a suitable reference.
Let me hammer this home:
Usually, we call methods which a reference (or value of) an object, instead the Where
method is requesting a reference to a method which is the same type of Func
.
Let's see a concrete example:
Scenario: Select all names beginning with M
To do this, I need to create a method that tests a given item, and return true
if it begins with M
:
class LinqWhereClause
{
public void WhereClause()
{
var names = new List<string>();
names.Add("Steve");
names.Add("Dave");
names.Add("Matt");
Func<string, bool> myFunc = new Func<string, bool>(MyWhereClause);
var thing = names.Where(myFunc);
}
public bool MyWhereClause(string item)
{
return item.StartsWith("M");
}
}
However, because of these features introduced in (introduced in C# 2*):
- Removing the awkward delegate syntax
- Anonymous methods, allowing you to define a delegate instance's action in-line
We can supply it in a way I suspect you're familiar with:
class LinqWhereClause
{
public void WhereClause()
{
var names = new List<string>();
names.Add("Steve");
names.Add("Dave");
names.Add("Matt");
Func<string, bool> myFunc = new Func<string, bool>(item => item.StartsWith("M"));
var thing = names.Where(myFunc);
}
}
* Surprising isn't it, this has been available since 2005!
Hopefully, you can see that the only difference is that we've compressed the method definition, moving towards a more fluid, expressive form of programming. Which, in conjunction with anonymous methods is the only purpose of Func.
There are 17 varieties of Func, each one specifying a different number of parameters to enable you to pick the right one for your task. There is also a sibling Action which has the same purpose expected for not returning a value.
So... why not have a glance over your codebase? How many times have you used Func
without giving it a second thought? Even better, how could you produce your own methods that accept functionality as a parameter in the form of Func
?