Table Of Contents
Why Delegates?
Delegates are used in the following cases:
- Delegates can be used to handle(call/invoke) multiple methods on a single event.
- Delegates can be used to define callback(asynchronous) methods.
- Delegates can be used for decoupling and implementing generic behaviors.
- Delegates can be invoked method at runtime.
- Delegates can be used in LINQ for parsing the
ExpressionTree
.
- Delegates can be used in different Design Pattern.
A delegate(known as function pointer in C/C++) is a references type that invokes single/multiple method(s) through the delegate instance. It holds a reference of the methods.
Delegate types are sealed and immutable type.
Types of Delegates
There are three types of delegates that can be used in C#.
- Single Delegate
- Multicast Delegate
- Generic Delegate
Single Delegate
Single delegate can be used to invoke a single method. In the given source code example, a delegate CalculateSimpleInterest
invokes a method getTotalInterest()
.
class Program
{
delegate double CalculateSimpleInterest(double p, double t, double r);
static CalculateSimpleInterest SI = getTotalInterest;
static void Main(string[] args)
{
double totalInterest;
totalInterest = SI.Invoke(120, 1, 3.25);
Console.WriteLine("Total Interest of $120
in a year at rate of 3.25% APR is {0}",totalInterest);
CalculateSimpleInterest D = new CalculateSimpleInterest(getTotalInterest);
totalInterest = D(120, 1, 3.25);
Console.WriteLine("Total Interest of $120
in a year at rate of 3.25% APR is {0}", totalInterest);
Console.ReadKey();
}
static double getTotalInterest(double p, double t, double r)
{
return (p * t * r) / 100;
}
}
Multicast Delegate
Multicast delegate can be used to invoke the multiple methods. The delegate instance can do multicasting (adding new method on existing delegate instance) using the + operator and – operator can be used to remove a method from a delegate instance. All methods will invoke in sequence as they are assigned.
In the given source code example, a delegate instance dObjSI
invokes the methods getTotalInterest()
, getInterestRatePerYear()
and getInterestTimeSpan()
.
class Program
{
delegate double CalculateSimpleInterest(double para1, double para2, double para3);
static CalculateSimpleInterest dObjSI = getTotalInterest;
static void Main(string[] args)
{
double SI;
SI = dObjSI.Invoke(120, 1, 3.25);
dObjSI += new CalculateSimpleInterest(getInterestRatePerYear);
double Rate=dObjSI.Invoke(SI, 120, 1);
Console.WriteLine("APR rate is {0}", Rate);
dObjSI += new CalculateSimpleInterest(getInterestTimeSpan);
double TimeSpan = dObjSI.Invoke(SI, 120, 3.25);
Console.WriteLine("Time Span is {0}", TimeSpan);
Console.ReadKey();
}
static double getTotalInterest(double p, double t, double r)
{
return (p * t * r) / 100;
}
static double getInterestRatePerYear(double SI, double p, double t)
{
return (SI * 100)/(p*t);
}
static double getInterestTimeSpan(double SI, double p, double r)
{
return (SI * 100) / (p * r);
}
}
Generic Delegate
Generic Delegate was introduced in .NET 3.5 that don't require to define the delegate instance in order to invoke the methods.
There are three types of generic delegates:
Generic Delegate: Func
The Func
delegate defines a method that can be called on arguments and returns a result. In the given code example, delegate Func<interest,double>
is defined with Interest type
as argument and double
as return type.
class Program
{
delegate double CalculateSimpleInterest(double para1, double para2, double para3);
static CalculateSimpleInterest dObjSI = getTotalInterest;
static void Main(string[] args)
{
double SI;
Func<interest,double> calcSI = SIObj =>(SIObj.P*SIObj.T*SIObj.R)/100;
Interest obj = new Interest();
obj.P = 120; obj.T = 1; obj.R = 3.25;
SI = calcSI(obj);
Console.WriteLine("Total Interest of $120 in a year at rate of 3.25% APR is {0}", SI);
Console.ReadKey();
}
}
class Interest
{
public double P { get; set; }
public double T { get; set; }
public double R { get; set; }
}
</interest,double>
Generic Delegate: Action
The Action
delegate defines a method that can be called on arguments but does not return a result. In the given code example, delegate Action<string>
is defined with string
as argument.
Action<string> MyAction = y => Console.Write(y);
MyAction("Hello");
Console.ReadKey();
Generic Delegate: Predicate
The Predicate delegate defines a method that can be called on arguments and always returns Boolean type result. In the given code example, delegate Predicate<string> checkValidDate
is defined with string
type as argument and returns bool
type.
class Program
{
static void Main(string[] args)
{
string date="05/12/20143";
Predicate<string> checkValidDate = d => IsDate(d) ;
if (checkValidDate(date))
{
Console.WriteLine("Valid Date");
}
else
{
Console.WriteLine("Invalid Date");
}
Console.ReadKey();
}
private static bool IsDate(string date)
{
DateTime dt;
return DateTime.TryParse(date,out dt);
}
}
</string>
Expression trees allow you to build code dynamically at runtime instead of statically typing it in the IDE and using a compiler. Expression Trees use generic delegates to create and parse the expressions.
Expression trees are used in the following cases:
- Expression trees can be used to create LINQ to SQL and EF to SQL.
- Expression trees can be used for ASP.NET MVC's HTML extensions.
- Expression trees can be used to determine the selected property or field in MVC.
In the given code example, a expression (3+5)-(4-2) is divided into three expressions as Exp1 for (3+5), Exp2 for (4-2) and Exp3 for adding Exp1 and Exp2.
The expression Expression.Lambda<func<int>>(resultexp).compile()()
uses Func
generic delegate to parse the expressions.
class Program
{
static void Main(string[] args)
{
BinaryExpression Exp1 = Expression.MakeBinary(ExpressionType.Add, Expression.Constant(3),
Expression.Constant(5));
BinaryExpression Exp2 = Expression.MakeBinary(ExpressionType.Subtract, Expression.Constant(4),
Expression.Constant(2));
BinaryExpression resultExp = Expression.MakeBinary(ExpressionType.Subtract, Exp1, Exp2);
int result = Expression.Lambda<func<int>>
(resultexp).compile()(); console.writeline("result="{0}",">
Difference Between Each Type of Generic Delegate
| Func | Action | Predicate |
Arguments |
Yes |
Yes |
Yes |
Returns |
Yes |
No |
Boolean Type Only |
History
- 13th May, 2014: Initial version