C# - Func Delegate
The Func
delegate is a built-in generic delegate type in C#. It's primarily used to represent methods that have a return value. The Func
delegate can have zero to sixteen input parameters, and it always has one return type.
The type arguments for the Func
delegate are the input parameter types followed by the return type. For instance, Func<int, bool>
represents a method that takes an int
as a parameter and returns a bool
.
using System;
namespace FuncDelegateExample
{
class Program
{
static void Main()
{
// Using Func delegate to point to a method that takes an int and returns a bool
Func<int, bool> isEven = CheckIfEven;
// Invoking the delegate
bool result = isEven(10); // Should return true
Console.WriteLine($"Is 10 even? {result}");
// Using lambda expression with Func
Func<int, int, int> add = (x, y) => x + y;
int sum = add(5, 3); // Should return 8
Console.WriteLine($"5 + 3 = {sum}");
}
static bool CheckIfEven(int number)
{
return number % 2 == 0;
}
}
}
The output of the program will be:
Is 10 even? True
5 + 3 = 8
-
The line
Func<int, bool> isEven = CheckIfEven;
creates a Func
delegate that points to the CheckIfEven
method. This method takes an integer argument and returns a boolean.
-
The line
bool result = isEven(10);
invokes the delegate with the argument 10
, checks if it is even, and stores the result (True
) in result
.
-
The line
Console.WriteLine($"Is 10 even? {result}");
prints "Is 10 even? True" to the console.
-
The line
Func<int, int, int> add = (x, y) => x + y;
uses a lambda expression to create another Func
delegate for addition.
-
The line
int sum = add(5, 3);
invokes this delegate with the arguments 5
and 3
, performs the addition, and stores the result (8
) in sum
.
-
The line
Console.WriteLine($"5 + 3 = {sum}");
prints "5 + 3 = 8" to the console.
Remember, the last type parameter of the Func
delegate always indicates the return type. In the isEven
delegate, for example, the int
indicates the input type and bool
indicates the return type.
Func Delegate Real-time Example:
One common real-world scenario where Func
delegates shine is in data filtering and transformations, especially with databases and collections. Let's take an example from the world of e-commerce.
Scenario:
Suppose you're building an e-commerce platform and you want to provide a feature where users can filter products based on their preferences. The filter criteria can be anything: price range, product rating, brand, etc.
Using Func
delegates, you can create dynamic filter criteria without having to write a separate method for each type of filter.
using System;
using System.Collections.Generic;
using System.Linq;
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public float Rating { get; set; }
public string Brand { get; set; }
}
public class ProductFilter
{
public List<Product> FilterByCriteria(List<Product> products, Func<Product, bool> criteria)
{
return products.Where(criteria).ToList();
}
}
public class Program
{
public static void Main()
{
List<Product> products = new List<Product>
{
new Product { Id = 1, Name = "Laptop", Price = 1000m, Rating = 4.5f, Brand = "BrandA" },
new Product { Id = 2, Name = "Mobile", Price = 500m, Rating = 4.2f, Brand = "BrandB" },
new Product { Id = 3, Name = "Headphones", Price = 100m, Rating = 4.8f, Brand = "BrandA" },
// ... other products
};
var filter = new ProductFilter();
// Filter products by price
var expensiveProducts = filter.FilterByCriteria(products, p => p.Price > 500m);
Console.WriteLine($"Expensive Products (Price > 500): {expensiveProducts.Count}");
// Filter products by rating
var highlyRatedProducts = filter.FilterByCriteria(products, p => p.Rating > 4.5f);
Console.WriteLine($"Highly Rated Products (Rating > 4.5): {highlyRatedProducts.Count}");
// Filter products by brand
var brandAProducts = filter.FilterByCriteria(products, p => p.Brand == "BrandA");
Console.WriteLine($"BrandA Products: {brandAProducts.Count}");
}
}
In the example above:
- We have a
Product
class representing products in the e-commerce platform.
- The
ProductFilter
class contains a FilterByCriteria
method, which filters a list of products based on a Func
delegate.
- In the
Main
method, we have a list of products and we use the FilterByCriteria
method to filter products by different criteria: price, rating, and brand.
The output of the program will be:
Expensive Products (Price > 500): 1
Highly Rated Products (Rating > 4.5): 1
BrandA Products: 2
Explanation:
- A list of
Product
objects is created with three items.
- An instance of the
ProductFilter
class named filter
is created.
- The
FilterByCriteria
method is called three times with different filtering criteria:
- First, it filters products with a price greater than 500. The count of such products is 1, which includes the "Laptop".
- Second, it filters products with a rating greater than 4.5. The count of such products is 1, which includes the "Headphones".
- Third, it filters products with the brand "BrandA". The count of such products is 2, which includes "Laptop" and "Headphones".
By using Func
delegates, we can easily extend our filtering capabilities without having to modify the ProductFilter
class or add more methods for different filter criteria. The filtering logic is abstracted, making it reusable and maintainable.
- Syntax:
Func
is a generic delegate type defined in the System
namespace.
- Return Type: Unlike
Action
, Func
always returns a value. The last type parameter specifies the return type.
- Type Parameters:
Func
can take up to 16 input parameters (as of C# 9.0).
- Lambda Expressions: It is often used with lambda expressions for inline method definitions.
- Method Group Conversion: You can also assign existing methods to a
Func
delegate as long as the method signature matches the delegate signature.
- LINQ: It is commonly used in LINQ queries for filtering, projection, etc.
- Strongly-Typed: Being a generic delegate,
Func
is strongly-typed, ensuring type safety.
- Null Handling: Always check for
null
before invoking a Func
delegate, or use the null conditional operator (?.
).
- Multi-threading: Consider thread-safety when using
Func
delegates in multi-threaded applications.
- Higher-Order Functions:
Func
delegates can be passed as arguments to methods or returned from methods, making it useful for higher-order functions.
- Immutability: Like all delegates,
Func
delegates are immutable. When you combine or remove delegates, a new delegate instance is created.
- Common Usage: Used extensively for callbacks, predicates, or functionalities that need to be passed around as parameters or stored as variables.
Remember, while Func
and Action
delegates are convenient, they might not be as self-explanatory as custom delegate types, which could impact code readability. Always weigh the pros and cons when deciding to use them.