What is Func?
Func in short is parameterized
delegate. In C#, a delegate instance points towards a method. When a caller
invokes the delegate, it calls its target method. This way, the caller is not
invoking the target method rather invoking the delegate which can call the target
method. We do it because it creates an abstraction on invoking the target
method. We of course always can invoke a method directly but decoupling of the
client and target method is sometimes a need or gives us more flexibility to
make things clean and simple.
We can use Func delegate to represent a method
that can be passed as a parameter without explicitly declaring a custom
delegate.
Why I said it's a parameterized delegate:
delegate TResult Func <out TResult> ();
delegate TResult Func <in T, out TResult> (T arg);
delegate TResult Func <in T1, in T2, out TResult> (T1 arg1, T2 arg2);
... and so on, up to T16
The Problem
To understand use of Func, we must understand what
problems it can solve. Let's say we have an int array here.
IEnumerable<int> numbers = new[] {3, 4, 7, 1, 8, 10, 21, 5, 9, 11, 14, 19};
It's a simple array. If we want to print the numbers which are greater
than 10, we will write a method like below. We will send
the array as parameter and will get another array which will contain the
numbers that are greater than 10.
public IEnumerable<int> GetGreaterThanTen(IEnumerable<int>
numbers)
{
foreach (int number in numbers)
if (number > 10)
yield return number;
}
…and then if we want to print the numbers which will have result less
than 1 after divided by 5, we will write a method:
public IEnumerable<int> GetGreterThanOneAfterDevidedByFive(IEnumerable<int>
numbers)
{
foreach (int number in numbers)
if ((number/5) > 1)
yield return number;
}
The Solution
In the above way, we will add more methods as the new conditions come.
But there is a better way to face this problem. We can and should use delegate
to avoid rewriting a fresh method every time we get by a new rule we want to
apply for this array. Use of Func as method parameter we will have
the leverage to decouple the caller and the function we want to use. Func will pass the rule to the method body and will
apply it.
Hide Copy Code
public IEnumerable<int> GetNumbers(IEnumerable<int> numbers,
Func<int, bool> numberResolver)
{
foreach (int number in numbers)
if (numberResolver(number))
yield return number;
}
IEnumerable<int> numbers = new[] {3, 4, 7, 1, 8, 10, 21, 5, 9, 11, 14, 19};
IEnumerable<int> greaterThanTen =
GetNumbers(numbers, x=>x>10);
greaterThanTen.ToList().ForEach(Console.WriteLine);
Console.WriteLine("\n");
IEnumerable<int>
greaterThanOneAfterDividedByFive = GetNumbers(numbers, n => (n/5) > 1);
greaterThanOneAfterDividedByFive.ToList().ForEach(Console.WriteLine);
The above code is pretty self-explanatory. Func uses Lambda expression which is essentially an anonymous function.
For extensibility standpoint, Func is very effective. While writing
various extension methods, you will find it extremely useful.
Getting TResult
The x-factor of Func is probably Func<TResult>. For functional programming, the return type has
great use. For example:
Hide Copy Code
Func<int, int> square = num => num*num;
Console.WriteLine(square(3));
…also, we can bring the previous example here see a bit more complex
scenario.
Hide Copy Code
IEnumerable<int> numbers = new[] { 3, 4, 7, 1, 8, 10, 21, 5, 9, 11, 14, 19 };
Func<IEnumerable<int>,
IEnumerable<int>> getGreaterThanTen = nums =>
{
var array = new List<int>();
nums.ToList().ForEach(x =>
{
if (x > 10)
array.Add(x);
});
return array;
};
var result = getGreaterThanTen(numbers);
result.ToList().ForEach(Console.WriteLine);
No comments:
Post a Comment