Unit - 4
C# Delegates
In C#, delegate is a reference to the method. It works like function
pointer in C and C++. But it is objected-oriented, secured and type-safe
than function pointer.
For static method, delegate encapsulates method only. But for instance
method, it encapsulates method and instance both.
The best use of delegate is to use as event.
Internally a delegate declaration defines a class which is the derived class
of System.Delegate.
Provides a good way to encapsulate the methods.
Delegates are the library class in System namespace.
These are the type-safe pointer of any method.
Delegates allow methods to be passed as parameters.
Delegates provide a way to execute methods at run-time.
There are three steps for defining and using delegates:
1. Declaration
A delegate is declared by using the keyword delegate, otherwise it
resembles a method declaration.
Syntax-
[modifier] delegate [return_type] [delegate_name] ([parameter_list]);
Example :-- public delegate int display(int a, int b);
2. Instantiation
To create a delegate instance, we need to assign a method (which has the
same signature as a delegate) to delegate.
Syntax:-
[delegate_name] [instance_name] = new [delegate_name](calling_method_name);
Example:-
display GFG = new display (Geeks);
3. Invocation
Invoking a delegate is like invoking a regular method.
del_obj1(100, 40);
Types of delegates
1. Single cast delegate
A single cast delegate holds the reference of only a single method.
This means that when you invoke the delegate, it will call only one
method.
Program:
// Define a delegate type
Using System;
public delegate void MyDelegate(string message);
class Program
// Method that matches the delegate's signature
public static void DisplayMessage(string message)
Console.WriteLine(message);
}
static void Main(string[] args)
// Instantiate the delegate with a method
MyDelegate del = DisplayMessage;
// Invoke the delegate
del("Hello, Single-Cast Delegate!");
Output:- Hello, Single-Cast Delegate!
2. Multicast delegate
A delegate who holds the reference of more than one method is
called a multi-cast delegate.
A multicast delegate only contains the reference of methods whose
return type is void.
The + and += operators are used to combine delegate instances.
Multicast delegates are considered equal if they reference the same
methods in the same order.
Delegate objects can be composed using the "+" operator.
A composed delegate calls the two delegates it was composed
from. Only delegates of the same type can be composed.
The "-" operator can be used to remove a component delegate from
a composed delegate
Program
using System;
public delegate void MyDelegate(int a,int b);
class Program
{ // Two methods that match the delegate's signature
public static void MethodA(int a,int b)
int c=a+b;
Console.WriteLine(c);
public static void MethodB(int a,int b)
int c=a-b;
Console.WriteLine(c);
static void Main(string[] args)
// Instantiate the multicast delegate
MyDelegate del = MethodA; // Assign MethodA to the delegate
// Add MethodB to the delegate
del += MethodB; // Now the delegate points to both MethodA and
MethodB
// Invoke the delegate
del(20,10);
}}
Output:-
30
10
Generic Delegates –
In C#, generic delegates provide a way to define delegates that can work with any data type.
The most commonly used built-in generic delegates are Action<T>, Predicate<T>, and
Func<T>.
1. Action<T>
Definition: Action<T> is a delegate type that represents a method that takes one parameter of
type T and does not return a value. It can also be used with multiple parameters (e.g.,
Action<T1, T2>).
Usage: Use Action<T> when you want to perform an operation without needing to return a
result.
Example Program:
using System;
class Program
{
static void Main()
{
// Define an Action delegate to print a message
Action<string> printMessage = message => Console.WriteLine(message);
// Invoke the delegate
printMessage("Hello, Action Delegate!");
}
}
Output:
Hello, Action Delegate!
2. Predicate<T>
Definition: Predicate<T> is a delegate type that represents a method that takes one parameter
of type T and returns a boolean value. It’s often used for filtering or checking conditions.
Usage: Use Predicate<T> when you want to determine whether a certain condition is met.
Example Program:
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// Create a list of integers
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
// Define a Predicate delegate to check for even numbers
Predicate<int> isEven = number => number % 2 == 0;
// Find all even numbers using the Predicate
List<int> evenNumbers = numbers.FindAll(isEven);
// Print the even numbers
Console.WriteLine("Even Numbers: " + string.Join(", ", evenNumbers));
}
}
Output:
Even Numbers: 2, 4, 6
3. Func<T, TResult>
Definition: Func<T, TResult> is a delegate type that represents a method that takes one or
more parameters of type T and returns a value of type TResult. It can take up to 16
parameters.
Usage: Use Func<T, TResult> when you want to perform a calculation or transformation and
get a result.
Example Program:
using System;
class Program
{
static void Main()
{
// Define a Func delegate to add two integers
Func<int, int, int> add = (x, y) => x + y;
// Invoke the delegate and get the result
int result = add(5, 10);
// Print the result
Console.WriteLine($"The sum is: {result}");
}
}
Output: The sum is: 15
Lambda Expressions
Lambda expressions in C# are a concise way to represent anonymous functions. They are
especially useful for creating delegates or expression tree types. Let's explore lambda
expressions and statement lambdas in detail, including parameters, return types, and captures.
1. Lambda Expressions
Definition: A lambda expression is a shorthand syntax for writing anonymous methods using
the => operator. The left side of the => defines the parameters, and the right side defines the
expression or statement block.
Syntax
Expression Lambda: (parameters) => expression
Statement Lambda: (parameters) => { statements }
2. Expression Lambdas
Expression lambdas contain a single expression. The return type is inferred from the
expression.
Example:
using System;
class Program
{
static void Main()
{
// Expression lambda to calculate square
Func<int, int> square = x => x * x;
// Invoke the lambda and print the result
Console.WriteLine($"Square of 5 is: {square(5)}");
// Output: Square of 5 is: 25
}
}
3. Statement Lambdas
Statement lambdas can contain multiple statements within a block. You must explicitly
specify the return type if it is not clear.
Example:
using System;
class Program
{
static void Main()
{
// Statement lambda to calculate the sum
Func<int, int, int> add = (x, y) =>
{
int result = x + y;
return result;
};
// Invoke the lambda and print the result
Console.WriteLine($"Sum of 3 and 4 is: {add(3, 4)}");
// Output: Sum of 3 and 4 is: 7
}
}
4. Parameters
Lambda expressions can have zero or more parameters. The type of the parameters can be
inferred, but you can also specify them explicitly.
Example:
using System;
class Program
{
static void Main()
{
// Lambda with inferred parameter type
Func<double, double> half = x => x / 2;
// Lambda with explicitly typed parameters
Func<int, int, int> multiply = (int a, int b) => a * b;
Console.WriteLine($"Half of 10 is: {half(10)}"); // Output: Half of 10 is: 5
Console.WriteLine($"Product of 3 and 4 is: {multiply(3, 4)}"); // Output: Product of 3
and 4 is: 12
}
}
5. Return Type
The return type of a lambda expression is inferred from the expression itself or can be
explicitly defined when using a statement lambda.
Example:
using System;
class Program
{
static void Main()
{
// Implicit return type
Func<int, bool> isEven = num => num % 2 == 0;
// Explicit return type
Func<int, double> squareRoot = (int num) =>
{
return Math.Sqrt(num);
};
Console.WriteLine($"Is 4 even? {isEven(4)}");
// Output: Is 4 even? True
Console.WriteLine($"Square root of 16 is: {squareRoot(16)}"); //
Output: Square root of 16 is: 4
}
}
6. Captures
Capturing occurs when a lambda expression uses variables from its containing scope. This is
useful for accessing variables that are defined outside the lambda.
Example:
using System;
class Program
{
static void Main()
{
int factor = 10;
// Capturing the outer variable 'factor'
Func<int, int> multiplyByFactor = x => x * factor;
Console.WriteLine($"5 multiplied by factor {factor} is: {multiplyByFactor(5)}");
// Output: 5 multiplied by factor 10 is: 50
// Changing the value of 'factor'
factor = 20;
Console.WriteLine($"5 multiplied by new factor {factor} is: {multiplyByFactor(5)}");
// Output: 5 multiplied by new factor 20 is: 100
}
}
Event
Events are a special kind of multicast delegate that can only be invoked from
within the class (or derived classes) or struct where they are declared (the
publisher class).
Let’s break down the concept of events into the following sections:
1. Defining Events
To define an event, you typically do the following:
1. Create a delegate that defines the signature for the event handler.
2. Declare an event of that delegate type.
Example of Defining an Event
using System;
public class Publisher
{
// Step 1: Define a delegate
public delegate void Notify();
// Step 2: Declare an event
public event Notify OnNotify;
public void RaiseEvent()
{
// Raise the event
OnNotify?.Invoke();
}
}
2. Raising Events
To raise an event, you call the event handler using the Invoke method or the shorthand
OnNotify?.Invoke() to safely call the event if there are any subscribers.
public class Subscriber
{
public void Subscribe(Publisher publisher)
{
publisher.OnNotify += Respond; // Subscribe to the event
}
private void Respond()
{
Console.WriteLine("Event has been raised!");
}
}
3. Standard Events
C# provides a standard event pattern which is more commonly used. It uses EventHandler or
EventHandler<T> delegate types. This is beneficial because it has a predefined method
signature.
Example of a Standard Event
using System;
public class Publisher
{
// Step 1: Define a standard event using EventHandler
public event EventHandler<EventArgs> OnNotify;
public void RaiseEvent()
{
// Step 2: Raise the event
OnNotify?.Invoke(this, EventArgs.Empty);
}
}
public class Subscriber
{
public void Subscribe(Publisher publisher)
{
publisher.OnNotify += Respond; // Subscribe to the event
}
private void Respond(object sender, EventArgs e)
{
Console.WriteLine("Standard event has been raised!");
}
}
4. Custom Events
You can also create custom event arguments by deriving from EventArgs. This allows you to
pass additional data when the event is raised.
Example of a Custom Event
using System;
public class MyEventArgs : EventArgs
{
public string Message { get; }
public MyEventArgs(string message)
{
Message = message;
}
}
public class Publisher
{
public event EventHandler<MyEventArgs> OnNotify;
public void RaiseEvent(string message)
{
OnNotify?.Invoke(this, new MyEventArgs(message));
}
}
public class Subscriber
{
public void Subscribe(Publisher publisher)
{
publisher.OnNotify += Respond; // Subscribe to the event
}
private void Respond(object sender, MyEventArgs e)
{
Console.WriteLine($"Custom event has been raised with message:
{e.Message}");
}
}
5. Putting It All Together
Here's a complete program that demonstrates defining, raising, and using standard and
custom events.
using System;
public class MyEventArgs : EventArgs
{
public string Message { get; }
public MyEventArgs(string message) => Message = message;
}
public class Publisher
{
// Standard event
public event EventHandler<EventArgs> OnStandardNotify;
// Custom event
public event EventHandler<MyEventArgs> OnCustomNotify;
public void RaiseStandardEvent()
{
OnStandardNotify?.Invoke(this, EventArgs.Empty);
}
public void RaiseCustomEvent(string message)
{
OnCustomNotify?.Invoke(this, new MyEventArgs(message));
}
}
public class Subscriber
{
public void Subscribe(Publisher publisher)
{
publisher.OnStandardNotify += RespondToStandardEvent;
publisher.OnCustomNotify += RespondToCustomEvent;
}
private void RespondToStandardEvent(object sender, EventArgs e)
{
Console.WriteLine("Standard event has been raised!");
}
private void RespondToCustomEvent(object sender, MyEventArgs e)
{
Console.WriteLine($"Custom event has been raised with message:
{e.Message}");
}
}
class Program
{
static void Main()
{
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();
subscriber.Subscribe(publisher);
publisher.RaiseStandardEvent();
publisher.RaiseCustomEvent("Hello, World!");
}
}
Output
Standard event has been raised!
Custom event has been raised with message: Hello, World!