With delegates, you can reference methods and build algorithms that work, regardless of what those methods are.
If you're familiar with C++, you might recognize this as being the same as function pointers. However, the difference with C# is that delegates are object-oriented and type safe.
Delegate Signatures
A delegate type is specified by a distinct signature, which includes a unique identifier, parameter list, and return type. The following code shows how to declare a delegate:
public delegate double UnitConversion(double from);
- Just like any other namespace element, delegates are declared as either public or internal, with a default of internal accessibility if the modifier is not specified.
- Delegates may also be declared as nested types, with accessibility modifiers allowable for their containing class or struct.
- Delegates do not have implementation. Instead, they are a skeleton defining the proper signature of delegate handler methods to which they may refer. The handler methods contain implementation that is executed when its referring delegate is invoked.
Dynamic Delegate Invocation
Sometimes you don't know what method you want to execute until runtime. One option is to use a delegate, which allows you to separate implementation of method logic from the controlling application logic.
using System;
// 1. Define delegate.
public delegate double UnitConversion(double from);
class DynamicDelegates
{
//
// 2. Define handler methods.
//
public double FeetToInches(double feet)
{
return feet * 12;
}
public double YardsToInches(double yards)
{
return yards * 36;
}
// Factory method for returning delegate
public UnitConversion GetConversionMethod(string conversionType)
{
UnitConversion conversion;
switch (conversionType)
{
case "1":
conversion = new UnitConversion(FeetToInches);
break;
case "2":
conversion = new UnitConversion(YardsToInches);
break;
default:
throw new ArgumentException("Unrecognized conversion type: " + conversionType, conversionType);
}
return conversion;
}
// show menu and return units to convert and type of conversion
public void GetUserInput(out double units, out string conversionType)
{
Console.WriteLine("Units Converter");
Console.WriteLine("------------------------\n");
Console.WriteLine("1 - Feet to Inches");
Console.WriteLine("2 - Yards to Inches");
Console.Write("\nPlease select conversion option (1 or 2): ");
conversionType = Console.ReadLine();
Console.Write("\n\nPlease enter units: ");
units = Double.Parse(Console.ReadLine());
}
static void Main()
{
DynamicDelegates dels = new DynamicDelegates();
double units;
string conversionType;
dels.GetUserInput(out units, out conversionType);
// 3. get delegate instance via factory method.
UnitConversion doConversion = dels.GetConversionMethod(conversionType);
// 4. Use delegate just like a method.
double inches = doConversion(units);
Console.WriteLine("\n{0} Units = {1} Inches.\n", units, inches);
Console.ReadLine();
}
}
// 1. Define delegate.
public delegate double UnitConversion(double from);
class DynamicDelegates
{
//
// 2. Define handler methods.
//
public double FeetToInches(double feet)
{
return feet * 12;
}
public double YardsToInches(double yards)
{
return yards * 36;
}
// Factory method for returning delegate
public UnitConversion GetConversionMethod(string conversionType)
{
UnitConversion conversion;
switch (conversionType)
{
case "1":
conversion = new UnitConversion(FeetToInches);
break;
case "2":
conversion = new UnitConversion(YardsToInches);
break;
default:
throw new ArgumentException("Unrecognized conversion type: " + conversionType, conversionType);
}
return conversion;
}
// show menu and return units to convert and type of conversion
public void GetUserInput(out double units, out string conversionType)
{
Console.WriteLine("Units Converter");
Console.WriteLine("------------------------\n");
Console.WriteLine("1 - Feet to Inches");
Console.WriteLine("2 - Yards to Inches");
Console.Write("\nPlease select conversion option (1 or 2): ");
conversionType = Console.ReadLine();
Console.Write("\n\nPlease enter units: ");
units = Double.Parse(Console.ReadLine());
}
static void Main()
{
DynamicDelegates dels = new DynamicDelegates();
double units;
string conversionType;
dels.GetUserInput(out units, out conversionType);
// 3. get delegate instance via factory method.
UnitConversion doConversion = dels.GetConversionMethod(conversionType);
// 4. Use delegate just like a method.
double inches = doConversion(units);
Console.WriteLine("\n{0} Units = {1} Inches.\n", units, inches);
Console.ReadLine();
}
}
Invoking Multiple Delegates
Among the things you can do with delegates is invoke several of them, all at the same time. Delegates have internal storage, called an invocation list, that holds multiple other delegates.
Most of the time, when invoking multiple delegates, the handler methods will be defined to return type void. This is because when they are invoked, only the last delegate in the list will return the value, which has few benefits, if any. If you need to capture results from each handler method, it's best to set up a collection or some other state object where the results could be recorded and retrieved after the delegate invocation completes.
using System;
using System.Collections.Generic;
// 1. Define delegate.
public delegate double UnitConversion(double from);
class MultipleDelegatesViaDelegates
{
//
// 2. Define handler methods.
//
public double FeetToInches(double feet)
{
Console.WriteLine("FeetToInches:{0}", feet * 12);
return feet * 12;
}
public double YardsToInches(double yards)
{
Console.WriteLine("YardsToInches:{0}", yards * 36);
return yards * 36;
}
// Factory method for returning delegate
public UnitConversion GetConversionMethod(string conversionType)
{
UnitConversion conversion;
switch (conversionType)
{
case "1":
conversion = new UnitConversion(FeetToInches);
break;
case "2":
conversion = new UnitConversion(YardsToInches);
break;
default:
throw new ArgumentException("Unrecognized conversion type: " + conversionType, conversionType);
}
return conversion;
}
// show menu and return units to convert and type of conversion
public void GetUserInput(out double units, out int[] conversionTypes)
{
Console.WriteLine("Units Converter");
Console.WriteLine("------------------------\n");
Console.WriteLine("1 - Feet to Inches");
Console.WriteLine("2 - Yards to Inches");
Console.Write("\nPlease select conversion option (1 or 2) and use blank as plitter: ");
string[] types = Console.ReadLine().Split(' ');
List<int> conversions = new List<int>();
foreach (string convertsiontype in types)
{
if (convertsiontype.Trim() != string.Empty )
conversions.Add( int.Parse(convertsiontype));
}
conversionTypes = conversions.ToArray();
Console.Write("\n\nPlease enter units: ");
units = Double.Parse(Console.ReadLine());
}
// displays a report by invoking all selected delegates
public void DoReport(double units, UnitConversion conversions)
{
Console.WriteLine("\nCONVERSION REPORT\n");
// 4. invoke all delegates synchronously and in order
conversions(units);
Console.WriteLine();
}
static void Main()
{
MultipleDelegatesViaDelegates dels = new MultipleDelegatesViaDelegates();
double units;
int[] conversionTypes;
dels.GetUserInput(out units, out conversionTypes);
UnitConversion conversions = null;
foreach (int conversionType in conversionTypes)
{
// 3. get delegate instances via factory method.
conversions += dels.GetConversionMethod(conversionType.ToString());
}
dels.DoReport(units, conversions);
Console.ReadLine();
}
}
using System.Collections.Generic;
// 1. Define delegate.
public delegate double UnitConversion(double from);
class MultipleDelegatesViaDelegates
{
//
// 2. Define handler methods.
//
public double FeetToInches(double feet)
{
Console.WriteLine("FeetToInches:{0}", feet * 12);
return feet * 12;
}
public double YardsToInches(double yards)
{
Console.WriteLine("YardsToInches:{0}", yards * 36);
return yards * 36;
}
// Factory method for returning delegate
public UnitConversion GetConversionMethod(string conversionType)
{
UnitConversion conversion;
switch (conversionType)
{
case "1":
conversion = new UnitConversion(FeetToInches);
break;
case "2":
conversion = new UnitConversion(YardsToInches);
break;
default:
throw new ArgumentException("Unrecognized conversion type: " + conversionType, conversionType);
}
return conversion;
}
// show menu and return units to convert and type of conversion
public void GetUserInput(out double units, out int[] conversionTypes)
{
Console.WriteLine("Units Converter");
Console.WriteLine("------------------------\n");
Console.WriteLine("1 - Feet to Inches");
Console.WriteLine("2 - Yards to Inches");
Console.Write("\nPlease select conversion option (1 or 2) and use blank as plitter: ");
string[] types = Console.ReadLine().Split(' ');
List<int> conversions = new List<int>();
foreach (string convertsiontype in types)
{
if (convertsiontype.Trim() != string.Empty )
conversions.Add( int.Parse(convertsiontype));
}
conversionTypes = conversions.ToArray();
Console.Write("\n\nPlease enter units: ");
units = Double.Parse(Console.ReadLine());
}
// displays a report by invoking all selected delegates
public void DoReport(double units, UnitConversion conversions)
{
Console.WriteLine("\nCONVERSION REPORT\n");
// 4. invoke all delegates synchronously and in order
conversions(units);
Console.WriteLine();
}
static void Main()
{
MultipleDelegatesViaDelegates dels = new MultipleDelegatesViaDelegates();
double units;
int[] conversionTypes;
dels.GetUserInput(out units, out conversionTypes);
UnitConversion conversions = null;
foreach (int conversionType in conversionTypes)
{
// 3. get delegate instances via factory method.
conversions += dels.GetConversionMethod(conversionType.ToString());
}
dels.DoReport(units, conversions);
Console.ReadLine();
}
}
Adding and Removing Delegates
- You can also remove delegates with the -= operator.
- Notice you can use the += syntax when adding delegates to the conversions delegate's invocation list.
You can add multiple delegates with the + operator. The following code shows another way to do this (assuming 3 conversion options were chosen by the user):
conversions =
dels.GetConversionMethod(conversionTypes[0]) +
dels.GetConversionMethod(conversionTypes[1]) +
dels.GetConversionMethod(conversionTypes[2]);- Additionally, you could use the original delegate instance to remove it from the invocation list. However, it would be wasteful to have extra delegates taking up space in your code just for this purpose. Notice, in the code above, that the new UnitConversion instantiation will remove a delegate from the conversions delegate list. If there happened to be two of the same delegate added to a delegate, then the last delegate of the type added would be the first to be removed.
- When a delegate is invoked, all of its constituent delegates are invoked synchronously in the same order that they were added.
- Another tip is to protect against the case where no constituent delegates have been added to a delegates invocation list. Before a delegate is instantiated or a delegate is assigned, its value is null. Invoking a null delegate causes a NullReferenceException exception to be thrown.
pending...
Summary
- Delegates allow you to refer to methods to dynamically invoke code and call multiple methods together. When executed, each member of a delegate's invocation list is executed synchronously, in the order they were added. A delegate's invocation list can be manipulated by adding and removing delegates.
- One of the primary uses of delegates in .NET is to implement callback methods for events. Events are safer than delegates for callbacks because they protect against accidental assignment and provide protection against direct invocation from outside code. Delegates allow you to refer to methods, invoke multiple methods simultaneously, and are the underlying mechanism for implementing C# events.
No comments:
Post a Comment