Define a delegate type 'Translator':
public delegate string Translator(string text);
Create a delegate instance and invoke it. This, in turn, calls a method the delegate is associated with:
// Define a delegate type. public delegate string Translator(string text); ... // Create a method with a signature compatible with the delegate type. public string GetGermanWord(string word) { string translatedText = ""; // execute a translation algorithm return translatedText; } ... // Create a delegate instance and associate it with the GetGermanWord method. Translator handler = new Translator(GetGermanWord); // Invoke the delegate instance. This, in turn, calls the GetGermanWord method. // Method #1 string helloInGerman = handler("Hello"); // Method #2 string helloInGerman = handler.Invoke("Hello");
Create a delegate instance and associate it with an anonymous method:
public delegate string Translator(string text); ... // Create a delegate instance. TextTranslator handler = delegate (string word) { string translatedText = ""; // execute a translation algorithm return translatedText; }; // Invoke the delegate instance. string translatedHello = handler("Hello");
Create a delegate instance of a built-in delegate type EventHandler:
// Declare a delegate of a built-in type EventHandler. EventHandler handler; // Method #1 - Create a delegate instance. handler = new EventHandler(MyMethod); // Method #2 - Create a delegate instance using method group conversion. handler = MyMethod; // Method #3 - Create a delegate instance with an anonymous method. handler = delegate (object sender, EventArgs e) { /* some code */ }; // Method #4 - Create a delegate instance with an anonymous method that does not require parameters. handler = delegate { /* some code */ }; // Invoke the delegate instance. handler(this, new EventArgs()); ... // A method with a signature compatible with the EventHandler delegate type. void MyMethod(object sender, EventArgs e) { /* some code */ }
Create a delegate instance by providing a method group. Note that the correct overload is picked based on the delegate signature:
public delegate string Translator(string text); ... public string GetGermanWord(string word) { string translatedText = ""; // execute a translation algorithm return translatedText; } // An overloaded method which signature is not compatible with the delegate. public void GetGermanWord(string word, int id) { // execute some code } ... // Create a delegate instance by providing a method group. Translator handler = GetGermanWord; // Invoke the delegate instance. The method 'string GetGermanWord(string word)' is called. string helloInGerman = handler("Hello");
Multicast capability of delegate instances means that a delegate instance can reference a list of methods.
If any of the actions in the invocation list throws an exception, it prevents the subsequent actions from being executed.
Combine and remove delegate instances:
public delegate void MethodDelegate(); public void Method1() { Console.WriteLine(1); } public void Method2() { Console.WriteLine(2); } public void Method3() { Console.WriteLine(3); } ... // Combine delegate instances. Delegate.Combine is called under the hood. MethodDelegate d = Method3; d += Method1; d += Method2; d(); // writes: 3,1,2 // Determine how many methods a delegate is going to call. int cnt = d.GetInvocationList().GetLength(0); // cnt == 3 // Remove a delegate instance. Delegate.Remove is called under the hood. d -= Method1; d(); // writes: 3,2 // Assign a single delegate instance. d = Method2; d(); // writes: 2 // The same as d = null because d has a single method. d -= Method2;
Generic delegate types Func take parameters of specified types and return a value of another specified type. For example, the Func<int,int> is a shorthand notation for a delegate that takes an int and returns an int. You can also use Action for specifying a delegate that doesn't have a return value.
delegate TResult Func <out TResult> (); delegate TResult Func <in T, out TResult> (T arg); delegate TResult Func <in T1, in T2, out TResult> (T1 arg1, T2 arg2); ... etc. delegate void Action (); delegate void Action <in T> (T arg); delegate void Action <in T1, in T2> (T1 arg1, T2 arg2); ... etc.
A few examples of Func and Action:
Action print = () => Console.WriteLine("Welcome"); print(); // displays 'Welcome' Action<string> print = s => Console.WriteLine(s); print("Hello"); // displays 'Hello' Action<string, int> print = (s, n) => { for (int i = 1; i <= n; ++i) Console.WriteLine(s); }; print("Hi", 3); // displays 'Hi' three times Func<string, int> fun = str => str.Length; int len = fun("test"); // len == 4 Func<int, int, string> fun = (x, y) => (x * y).ToString(); string result = fun(2, 3); // result == "6" Func<int, int> square = x => x * x; // the same as: x => { return x * x; }; int result = square(3); // result == 9
Example: A custom delegate type OperationDelegate with a generic type parameter:
using System; using System.Collections.Generic; namespace ConsoleTest { public delegate T OperationDelegate<T>(IEnumerable<T> sequence); static class SequenceHelper { public static T First<T>(IEnumerable<T> sequence) { foreach (T element in sequence) return element; throw new InvalidOperationException("Empty collection"); } public static T Last<T>(IEnumerable<T> sequence) { IEnumerator<T> e = sequence.GetEnumerator(); T v = default(T); while (e.MoveNext()) { v = e.Current; } return v; } } class Program { // A method that accepts a delegate of type OperationDelegate<T> as a parameter. private static void DisplayResults<T>(IEnumerable<T> values, OperationDelegate<T> operation) { Console.WriteLine(operation(values)); } static void Main() { int[] values = { 3, 7, 13, 8, 5 }; DisplayResults(values, SequenceHelper.First); // 3 DisplayResults(values, SequenceHelper.Last); // 5 } } }
Sometimes, instead of a delegate, we can use an interface to provide the same functionality. Compare the following two code snippets:
Snippet #1:
namespace ConsoleTest { public delegate string TranslatorHandler(string word); public class Translator { public static void Translate(string[] words, TranslatorHandler handler) { for (int i = 0; i < words.Length; ++i) words[i] = handler(words[i]); } } class Program { public static string SpanishTranslator(string word) { // ... translate the word return word; } static void Main() { string[] words = { "car", "dog", "octopus" }; Translator.Translate(words, SpanishTranslator); } } }
Snippet #2:
namespace ConsoleTest { public interface ITranslator { string Translate(string word); } public class Translator { public static void Translate(string[] words, ITranslator handler) { for (int i = 0; i < words.Length; ++i) words[i] = handler.Translate(words[i]); } } class SpanishTranslator : ITranslator { public string Translate(string word) { // ... translate the word return word; } } class Program { static void Main() { string[] words = { "car", "dog", "octopus" }; Translator.Translate(words, new SpanishTranslator()); } } }
The delegate may be a better choice if the interface defines only a single method or multicast capability is needed.
Also, the delegate is a better choice if the interface has to be implemented multiple times. For example, if we needed to provide PolishTranslator, GermanTranslator, and FrenchTranslator, we would have to create separate classes for each translator:
class PolishTranslator : ITranslator { public string Translate(string word) { /*...translate...*/ return word; } } class GermanTranslator : ITranslator { public string Translate(string word) { /*...translate...*/ return word; } } class FrenchTranslator : ITranslator { public string Translate(string word) { /*...translate...*/ return word; } }
C# Programming Guide: