
12. C# Delegates, Action, Functors, Predicates, Events
Built-In Delegates in C#
C# provides several built-in delegate types that simplify common programming patterns without explicitly declaring new delegate types for each scenario.
1) Action
An Action delegate references a method that returns void
. It can accept zero or more parameters, but it does not return a value.
Examples
- Action with no parameters:
Action noParamAction = () => Console.WriteLine("Hello from Action!"); noParamAction(); // prints "Hello from Action!"
- Action with parameters:
Action<int, string> multiParamAction = (number, text) => { Console.WriteLine($"Number: {number}, Text: {text}"); }; multiParamAction(42, "Sample");
2) Func
A Func delegate references a method that returns a value. It can accept zero or more parameters, with the last type argument representing the return type.
Examples
- Func with no parameters:
Func<string> getMessage = () => "Hello from Func!"; string message = getMessage(); // returns "Hello from Func!
- Func with parameters:
Func<int, int, int> sumFunc = (x, y) => x + y; int result = sumFunc(3, 5); // returns 8
3) Predicate
A Predicate<T> is a delegate that returns a bool
and accepts one parameter of type T
. It is often used for filtering or matching criteria.
Predicate<int> isEven = (x) => x % 2 == 0;
bool check = isEven(4); // check = true
Example with Array.FindAll
int[] numbers = { 1, 2, 3, 4, 5, 6 };
int[] evenNumbers = Array.FindAll(numbers, isEven);
foreach (int num in evenNumbers)
{
Console.WriteLine(num); // prints 2, 4, 6
}
In this example, Array.FindAll
uses the predicate (isEven
) to filter out elements from the array.
Events
An event in C# is built on top of delegates and provides a publish-subscribe (observer) mechanism. It’s commonly used to broadcast notifications that something has happened, allowing subscribers to handle the event without tight coupling.
Key Points
- Event is a Delegate
Under the hood, an event holds a delegate instance. However, an event restricts some operations compared to a raw delegate.
- Event Handlers
By convention, events often use the
EventHandler
orEventHandler<TEventArgs>
delegate, but any delegate can be used.
- Visibility
- Typically, you cannot invoke an event from outside the class that declares it.
- You can only add (
+=
) or remove (=
) handlers.
- Firing (Invoking) the Event
Inside the class that owns the event, you typically see code that checks if the event is non-null and then invokes it.
Example
public class Publisher
{
// Define an event of type Action or a custom delegate
public event Action OnDataProcessed;
public void ProcessData()
{
// Some processing logic
// ...
// Fire the event
OnDataProcessed?.Invoke();
}
}
public class Subscriber
{
public void Subscribe(Publisher pub)
{
// Register event handler
pub.OnDataProcessed += HandleDataProcessed;
}
private void HandleDataProcessed()
{
Console.WriteLine("Data processed event received.");
}
}
public static class Demo
{
public static void Main()
{
Publisher p = new Publisher();
Subscriber s = new Subscriber();
s.Subscribe(p);
p.ProcessData();
// The event triggers HandleDataProcessed in the Subscriber
}
}
Differences Between Delegates and Events
- Access Control:
- A raw delegate can be invoked by anyone who can access it.
- An event can typically be invoked only within the declaring class.
- Subscription:
- Both delegates and events support
+=
and=
to attach and detach handlers.
- Events do not allow direct assignment (
=
) from external code.
- Both delegates and events support
- Usage Context:
- Delegates are often used for function pointers or as parameters for callbacks.
- Events provide a structured model for the publisher/subscriber pattern.
Reference
The content in this document is based on the original notes provided in Azerbaijani. For further details, you can refer to the original document using the following link:
Original Note - Azerbaijani Version