Delegate Event Lambda Expressions and Miscellaneous






4.97/5 (15 votes)
Loosely coupled small examples of delegates, events, anonymous methods, and closures in C#
Introduction
This is a note of loosely coupled 11 small examples in the general subject of delegates, events, anonymous methods, and closures in C#. This note is intended to answer the following questions:
- What is a delegate?
- Can we define delegates with generic types?
- Where can we define delegates in a C# program?
- What is the
Target
property of a delegate instance? - What is
Func<T, TResult>
? - What is
Action<T>
? - What is an event?
- What is an anonymous method?
- What is a lambda expression?
- What is a closure? Does C# have closures?
- How can C# delegate instances "remember" the local variables of the method that creates them in-line?
- Does a delegate instance have any impact on garbage collection?
The 11 examples are really simple examples. If you do not have time to go through them, you can directly go to the summary section to check the answers. If you have some time later, you may come back for the examples.
Background
The attached Visual Studio solution is a simple WPF application. It is developed with Visual Studio 2013.
- The UI of the WPF application is implemented in MainWindow.xaml and MainWindow.xaml.cs files;
- Each of the classes in the Examples folder is one of the 11 examples.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Button Grid.Row="0" x:Name="btnBasicDelegateExample"
Click="btnBasicDelegateExample_Click">1. Basic Delegate Example</Button>
<Button Grid.Row="1" x:Name="btnGenericDelegateExample"
Click="btnGenericDelegateExample_Click">
2. Generic Delegate Example</Button>
<Button Grid.Row="2" x:Name="btnDelegateEverywhere"
Click="btnDelegateEverywhere_Click">3. Delegate Everywhere!</Button>
<Button Grid.Row="3" x:Name="btnDelegateTarget"
Click="btnDelegateTarget_Click">4. Delegate Target</Button>
<Button Grid.Row="4" x:Name="btnFuncExample"
Click="btnFuncExample_Click">5. Func Example</Button>
<Button Grid.Row="5" x:Name="btnActionExample"
Click="btnActionExample_Click">6. Action Example</Button>
<Button Grid.Row="6" x:Name="btnEventExample"
Click="btnEventExample_Click">7. Event Example</Button>
<Button Grid.Row="7" x:Name="btnAnonymousMethod"
Click="btnAnonymousMethod_Click">8. Anonymous Method</Button>
<Button Grid.Row="8" x:Name="btnLambdaExpression"
Click="btnLambdaExpression_Click">9. Lambda Expression</Button>
<Button Grid.Row="9" x:Name="btnClosureExample"
Click="btnClosureExample_Click">10. Closure Example</Button>
<Button Grid.Row="10" x:Name="btnClosureTargetExample"
Click="btnClosureTargetExample_Click">
11. Closure Delegate Target</Button>
</Grid>
The MainWindow.xaml declared 11 buttons. Each of the classes in the Examples folder implements a TryExample()
method. Clicking on each of the buttons will run the TryExample()
method of one of the examples.
using System.Windows;
using Delegate_Func_Action_M.Examples;
namespace Delegate_Func_Action_M
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnBasicDelegateExample_Click(object sender, RoutedEventArgs e)
{
new E_1_Basic_Delegate().TryExample();
}
private void btnGenericDelegateExample_Click(object sender, RoutedEventArgs e)
{
new E_2_Generic_Delegate().TryExample();
}
private void btnDelegateEverywhere_Click(object sender, RoutedEventArgs e)
{
new E_3_Delegate_Everywhere().TryExample();
}
private void btnDelegateTarget_Click(object sender, RoutedEventArgs e)
{
new E_4_Delegate_Target().TryExample();
}
private void btnFuncExample_Click(object sender, RoutedEventArgs e)
{
new E_5_Func_Example().TryExample();
}
private void btnActionExample_Click(object sender, RoutedEventArgs e)
{
new E_6_Action_Example().TryExample();
}
private void btnEventExample_Click(object sender, RoutedEventArgs e)
{
new E_7_Event_Example().TryExample();
}
private void btnAnonymousMethod_Click(object sender, RoutedEventArgs e)
{
new E_8_Anonymous_Method().TryExample();
}
private void btnLambdaExpression_Click(object sender, RoutedEventArgs e)
{
new E_9_Lambda_Expression().TryExample();
}
private void btnClosureExample_Click(object sender, RoutedEventArgs e)
{
new M_10_Closure().TryExample();
}
private void btnClosureTargetExample_Click(object sender, RoutedEventArgs e)
{
new M_11_Closure_Delegate_Target().TryExample();
}
}
}
Example 1 - Basic Delegate
People with C background can immediately find the similarity in functionality between delegates and function pointers. According to Microsoft documentation, a delegate is a type that represents references to methods with a particular parameter list and return type. The following is an example of the basic usage of delegates in C#.
using System.Text;
using System.Windows;
namespace Delegate_Func_Action_M.Examples
{
public delegate int PerformCalculation(int x, int y);
public class E_1_Basic_Delegate
{
private int Add(int i1, int i2)
{
return i1 + i2;
}
public void TryExample()
{
PerformCalculation calculate = Add;
StringBuilder sb = new StringBuilder("Result - ")
.Append(calculate(1, 2));
MessageBox.Show(sb.ToString(), this.GetType().Name);
}
}
}
- In the example, a delegate
delegate int PerformCalculation(int x, int y)
is defined. An instance of thePerformCalculation
can be used to reference any methods that takes twoint
input parameters and returns anint
value; - The
Add
method is assigned to the delegate instancecalculate
; - The
Add
method can be called through the delegate instancecalculate
.
Click on the button, we can see that the Add
method is called successfully through the calculate
delegate instance.
Example 2 - Generic Delegate
A delegate is a type that represents references to methods with a particular parameter list and return type. It should not be a surprise that delegates support generic programming. The following is an example of a delegate with generic typed parameters.
using System.Text;
using System.Windows;
namespace Delegate_Func_Action_M.Examples
{
public delegate T GenericPerformCalculation<T>(T i1, T i2);
public class E_2_Generic_Delegate
{
private int AddInteger(int i1, int i2)
{
return i1 + i2;
}
private string AddString(string s1, string s2)
{
StringBuilder sb = new StringBuilder(s1)
.Append(" - ").Append(s2);
return sb.ToString();
}
public void TryExample()
{
GenericPerformCalculation<int> intDelegate = AddInteger;
GenericPerformCalculation<string> strDelegate = AddString;
StringBuilder sb = new StringBuilder("Call <string> delegate: ")
.Append(strDelegate("First", "Second")).Append("\n")
.Append("Call <int> delegate: ").Append(intDelegate(1, 2));
MessageBox.Show(sb.ToString(), this.GetType().Name);
}
}
}
- In the example, the delegate
GenericPerformCalculation<T>
is defined with the generic typeT
; - The method
AddInteger
performs an integer addition; - The method
AddString
performs astring
addition (append); - Each method is assigned to an instance of the delegate
GenericPerformCalculation<T>
.
Click on the button, we can see that both delegate instances work fine. We can use generic types in delegates in C#.
Example 3 - Delegate Everywhere
This example is to show where we can define delegates in a C# program. According to Microsoft documentation, a delegate is a type
. So we can define delegates at the same level as classes, we can also define delegates inside a class. But we cannot define delegates inside a method.
using System.Text;
using System.Windows;
namespace Delegate_Func_Action_M.Examples
{
public delegate int TopLevelDelegate(int x, int y);
public class DelegateContainer
{
public delegate double InClassDelegate(double x, double y);
}
public class E_3_Delegate_Everywhere
{
private int IntAdd(int i, int j)
{
return i + j;
}
private double doubleAdd(double i, double j)
{
return i + j;
}
public void TryExample()
{
TopLevelDelegate intDelegate = IntAdd;
DelegateContainer.InClassDelegate doubleDelegate = doubleAdd;
StringBuilder sb = new StringBuilder("Result - ")
.Append(intDelegate(1, 2))
.Append(" - ")
.Append(doubleDelegate(1.1, 2.2));
MessageBox.Show(sb.ToString(), this.GetType().Name);
}
}
}
- In the example, the
TopLevelDelegate
is defined at the same level as the classes; - The
InClassDelegate
is defined in a class; - When
InClassDelegate
is used, it is used through the containing class nameDelegateContainer.InClassDelegate
.
Click on the button, we can see that both delegate definitions work fine. It is not shown here, but if you try to define a delegate inside a method, you will see a compilation error.
Example 4 - Delegate Target
A delegate
is a type. A delegate
type has a property Target
. According to Microsoft documentation, the Target
property is used to "get the class instance on which the current delegate invokes the instance method". The following sentences are from the Microsoft documentation.
- An instance method is a method that is associated with an instance of a class;
- A
static
method is a method that is associated with the class itself; - If the delegate invokes one or more instance methods, this property returns the target of the last instance method in the invocation list.
using System.Text;
using System.Windows;
namespace Delegate_Func_Action_M.Examples
{
internal delegate void SetDelegate(string v);
internal static class AStaticClass
{
public static string i;
// A static set method
public static void setI(string v)
{
i = v;
}
}
internal class AnIntanceClass
{
public string i;
// An instance set method
public void setI(string v)
{
i = v;
}
}
public class E_4_Delegate_Target
{
public void TryExample()
{
AnIntanceClass instance1 = new AnIntanceClass();
AnIntanceClass instance2 = new AnIntanceClass();
SetDelegate staticDelegate = AStaticClass.setI;
SetDelegate instance1Delegate = instance1.setI;
SetDelegate instance2Delegate = instance2.setI;
// Use the delegate to set the strings
staticDelegate("Static string");
instance1Delegate("Instance 1 string");
instance2Delegate("Instance 2 string");
StringBuilder sb = new StringBuilder()
.Append("Static string is set to = ")
.Append(AStaticClass.i).Append("\n")
.Append("Instance 1 string is set to = ")
.Append(instance1.i).Append("\n")
.Append("Instance 2 string is set to = ")
.Append(instance2.i).Append("\n")
.Append("\n");
// Check out the delegate target
string staticDelegateTarget
= (staticDelegate.Target == null) ? "null" : "not null";
string instance1DelegateTarget
= (instance1Delegate.Target == null) ? "null" :
(instance1Delegate.Target == instance1) ? "instance1"
: "not instance1";
string instance2DelegateTarget
= (instance2Delegate.Target == null) ? "null" :
(instance2Delegate.Target == instance2) ? "instance2"
: "not instance2";
sb.Append("staticDelegate Target is ")
.Append(staticDelegateTarget).Append("\n")
.Append("instance1Delegate Target is ")
.Append(instance1DelegateTarget).Append("\n")
.Append("instance2Delegate Target is ")
.Append(instance2DelegateTarget).Append("\n");
MessageBox.Show(sb.ToString(), this.GetType().Name);
}
}
}
- In this example, a delegate
void SetDelegate(string v)
is defined; - A
static
methodvoid setI(string v)
is created in thestatic
classAStaticClass
, which sets the value for the static variablei
; - An instance method
void setI(string v)
is created in the instance classAnIntanceClass
, which sets the value for the instance variablei
; - Three different delegate instances are declared. One references to the
static
method, the other two reference to the instance method on two differentAnIntanceClass
instances.
Click on the button, we can see that the string
values on the static
class and the instances of the AnIntanceClass
class are set correctly. We can also see the following:
- The
Target
property of the delegate instance referencing thestatic
method isnull
; - The
Target
property of the delegate instance referencing the instance method is the instance object through which the instance method is assigned to the delegate instance.
Example 5 - Func<T, TResult> Delegate
You may have seen the Func<T, TResult>
syntax in C# programs. You may have wondered what it is. According to Microsoft documentation, it is simply a pre-defined generic delegate in the System
namespace from the mscorlib.dll in the .NET framework. It encapsulates a method that has one parameter and returns a value of the type specified by the TResult
parameter.
using System;
using System.Text;
using System.Windows;
namespace Delegate_Func_Action_M.Examples
{
class E_5_Func_Example
{
private string decorateInt(int i)
{
StringBuilder sb = new StringBuilder(i.ToString())
.Append(" is decorated!");
return sb.ToString();
}
public void TryExample()
{
Func<int, string> aFunc = decorateInt;
StringBuilder sb = new StringBuilder(aFunc(100));
MessageBox.Show(sb.ToString(), this.GetType().Name);
}
}
}
- In the example, the
string decorateInt(int i)
method has a singleint
input parameter and returns astring
value; - Since
Func<T, TResult>
is pre-defined by Microsoft, we can simply assign thedecorateInt
method to an instance of theFunc<int, string>
delegate.
Click on the button, we can see that the decorateInt
method is called successfully through the Func<int, string>
delegate.
Example 6 - Action<T> Delegate
Same as the Func<T, TResult>
delegate, the Action<span xmlns=""><T></span>
is also a pre-defined generic delegate in the System
namespace from the mscorlib.dll in the .NET framework. It encapsulates a method that has a single parameter and does not return a value.
using System;
using System.Text;
using System.Windows;
namespace Delegate_Func_Action_M.Examples
{
class E_6_Action_Example
{
private void DisplayMessage(string msg)
{
MessageBox.Show(msg, this.GetType().Name);
}
public void TryExample()
{
Action<string> action = DisplayMessage;
action("Message to display");
}
}
}
- In the example, the
void DisplayMessage(string msg)
has a singlestring
input parameter and does not return a value; - Since
Action<T>
is pre-defined by Microsoft, we can simply assign theDisplayMessage
method to an instance of theAction<string>
delegate.
Click on the button, we can see that the DisplayMessage
method is called successfully through the Action<string>
delegate.
Example 7 - Event Example
In C#, events are implemented on top of delegates. According to Microsoft documentation, Events enable a class or object to notify other classes or objects when something of interest occurs. The class that sends (or raises) the event is called the publisher and the classes that receive (or handle) the event are called subscribers.
- The publisher determines when an event is raised;
- The subscribers determine what action is taken in response to the event;
- An event can have multiple subscribers;
- A subscriber can handle multiple events from multiple publishers;
- Events that have no subscribers are never raised;
- Events are typically used to signal user actions such as button clicks or menu selections in graphical user interfaces;
- When an event has multiple subscribers, the event handlers are invoked synchronously when an event is raised.
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text;
using System.Windows;
namespace Delegate_Func_Action_M.Examples
{
public delegate void ADelegate(EventArgument arg);
public class EventArgument
{
public EventArgument()
{
ProcessMessage = new List<string>();
}
public List<string> ProcessMessage { get; private set; }
}
public class EventPublisher
{
public event ADelegate aEvent;
public EventArgument eventArgument { get; private set; }
public EventPublisher()
{
eventArgument = new EventArgument();
}
[MethodImpl(MethodImplOptions.Synchronized)]
public void RaiseEvent()
{
if (aEvent != null)
{
aEvent(eventArgument);
}
}
}
public class EventSubscriber
{
private void Handler1(EventArgument arg)
{
arg.ProcessMessage.Add("Event handled by Handler1");
}
private void Handler2(EventArgument arg)
{
arg.ProcessMessage.Add("Event handled by Handler2");
}
public void SubscribleEvent(EventPublisher publisher)
{
publisher.aEvent += Handler1;
publisher.aEvent += Handler2;
}
}
public class E_7_Event_Example
{
public void TryExample()
{
EventPublisher publisher = new EventPublisher();
EventSubscriber subscriber = new EventSubscriber();
subscriber.SubscribleEvent(publisher);
publisher.RaiseEvent();
StringBuilder sb = new StringBuilder();
foreach (string s in publisher.eventArgument.ProcessMessage)
{
sb.Append(s).Append("\n");
}
MessageBox.Show(sb.ToString(), this.GetType().Name);
}
}
}
- In the example, a delegate
void ADelegate(EventArgument arg)
is defined; - In the
EventPublisher
class, an eventaEvent
is declared based on theADelegate
delegate. This event is fired in theRaiseEvent
method; - In the
EventSubscriber
class, theaEvent
can be subscribed in theSubscribleEvent
method. If the event is fired, both theHandler1
andHandler2
methods will be called to handle the event.
Click on the button, we can see that both methods registered as handlers of the event is called when the event fires.
Example 8 - Anonymous Method
According to Microsoft documentation, an anonymous function/method is an inline
statement or expression that can be used wherever a delegate type is expected. You can use it to initialize a named delegate or pass it instead of a named delegate type as a method parameter.
using System.Text;
using System.Windows;
namespace Delegate_Func_Action_M.Examples
{
public delegate int PerformAddition(int x, int y);
public class E_8_Anonymous_Method
{
public void TryExample()
{
PerformAddition addition = delegate(int i, int j)
{
return i + j;
};
StringBuilder sb = new StringBuilder("Result - ")
.Append(addition(1, 2));
MessageBox.Show(sb.ToString(), this.GetType().Name);
}
}
}
- In the example, a delegate
int PerformAddition(int x, int y)
is defined; - In the
TryExample
method, the instance of thePerformAddition
delegate is assigned an anonymous method created in-line using thedelegate
syntax.
Click on the button, we can see that the anonymous method is called successfully through the delegate instance.
Example 9 - Lambda Expression
According to Microsoft documentation, a lambda expression is an anonymous function/method. But it uses a different syntax than the regular anonymous function/methods.
using System.Text;
using System.Windows;
namespace Delegate_Func_Action_M.Examples
{
public delegate int PerformMultiplication(int x, int y);
public class E_9_Lambda_Expression
{
public void TryExample()
{
PerformMultiplication multiply = (i, j) =>
{
return i * j;
};
StringBuilder sb = new StringBuilder("Result - ")
.Append(multiply(1, 2));
MessageBox.Show(sb.ToString(), this.GetType().Name);
}
}
}
- In the example, a delegate
int PerformMultiplication(int x, int y)
is defined; - In the
TryExample
method, the instance of thePerformMultiplication
delegate is assigned an anonymous function created with lambda expression syntax.
Click on the button, we can see that the lambda expression is called successfully though the delegate instance.
Example 10 - Closures in C#
If you have some functional programming experience, you should have already known about "closures". Since a delegate is a type in C#, and we can pass instances of delegates among different methods/functions, we will have "closures" in C#. What is unfortunate though is that I cannot find an official documentation from Microsoft on closures in C#. To introduce you to closures, I will use the document from Mozilla for JavaScript, since the idea is the same. Even from Mozilla, the one sentence definition for closures is still pretty fuzzy.
Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created.
If you do not understand this definition, it is fine, because I also feel that I can interpret it in a lot of different ways. To get a concrete idea on closures, we do better take a look at an example.
using System;
using System.Text;
using System.Windows;
namespace Delegate_Func_Action_M.Examples
{
internal class AnIntanceClass4Closure
{
private int Multiplier = 2;
// This method returns a Func delegate
public Func<int, int> GetAFuncDelegate()
{
// variable declared in the method
int i = 0;
return j =>
{
i = i + Multiplier * j;
return i;
};
}
}
class M_10_Closure
{
public void TryExample()
{
// Get the delegate
AnIntanceClass4Closure instance = new AnIntanceClass4Closure();
Func<int, int> aFunc = instance.GetAFuncDelegate();
StringBuilder sb = new StringBuilder("i initial value = 0\n")
.Append("Add 1 * 2 = ").Append(aFunc(1)).Append("\n")
.Append("Add 2 * 2 = ").Append(aFunc(2)).Append("\n")
.Append("Add 3 * 2 = ").Append(aFunc(3)).Append("\n")
.Append("Add 4 * 2 = ").Append(aFunc(4)).Append("\n");
MessageBox.Show(sb.ToString(), this.GetType().Name);
}
}
}
- In the example, we have a class
AnIntanceClass4Closure
which has a methodGetAFuncDelegate
; - The
GetAFuncDelegate
function returns an instance of theFunc<int, int>
delegate. The delegate instance is created in-line inside theGetAFuncDelegate
by a lambda expression; - Because of the Lexical/static scoping rule, the lambda expression referenced the member variable
Multiplier
and the local variablei
declared in theGetAFuncDelegate
method.
It is a common knowledge that the local variable i
in the GetAFuncDelegate
method is created in the stack. When the method returns, the variable i
will be cleared out from the memory. But what will happen if we invoke the delegate instance returned by the GetAFuncDelegate
method? Will it be accessing a memory address that is already cleared?
Click on the button, we can see that the delegate instance is invoked just fine. If we check the result of the multiple invocations of the delegate instance, we can see that it "remembered" the local variable i
that should have already been cleared out from the memory in the sense that it is created in the stack and should be collected immediately after the method GetAFuncDelegate
returns.
- When we have a delegate instance created in-line by an anonymous function/lambda expression in a method, in the lexical scoping context, if the delegate instance references local variables declared in the method, the delegate instance can "remember" the local variables after the method that created it returns;
- This example shows that C# does have closures.
Example 11 - Closure Delegate Target
This example is intended to find some more information on how C# delegate instances remember the local variables. According to Microsoft documentation, the Target
property of a delegate instance is the class instance on which the current delegate invokes the instance method. But how can the class instance keep the information of local variables declared in a method after it returns?
using System;
using System.Text;
using System.Windows;
namespace Delegate_Func_Action_M.Examples
{
internal class AnIntanceClass4ClosureTarget
{
private int Multiplier = 2;
// This method returns a Func delegate
public Func<int, int> GetAFuncDelegate()
{
// variable declared in the method
int i = 0;
return j =>
{
i = i + Multiplier * j;
return i;
};
}
}
public class M_11_Closure_Delegate_Target
{
public void TryExample()
{
// Get the delegate
AnIntanceClass4ClosureTarget instance = new AnIntanceClass4ClosureTarget();
Func<int, int> aFunc = instance.GetAFuncDelegate();
StringBuilder sb = new StringBuilder();
Object target = aFunc.Target;
sb.Append((target == null) ? "Target is null" :
"Target is not null").Append("\n")
.Append((target == instance) ? "Target is instance"
: "Target is not instance").Append("\n")
.Append("Target is ").Append(target.ToString());
MessageBox.Show(sb.ToString(), this.GetType().Name);
}
}
}
- In the example, the
AnIntanceClass4ClosureTarget
class is the same as the example 10; - In the
TryExample
method, we will try to take a look at theTarget
property of the delegate instance returned by theGetAFuncDelegate
method.
Click on the button, we can find that the Target
property is not null
. It is not the instance of the AnIntanceClass4ClosureTarget
class. It is something like Delegate_Func_Action_M.Examples.AnIntanceClass4ClosureTarget+<>c__DisplayClass1
. This so called Delegate_Func_Action_M.Examples.AnIntanceClass4ClosureTarget+<>c__DisplayClass1
should have remembered the local variable i
. I have to be honest that I have not find an official document from Microsoft on how they let the delegate instances to remember the local variables. This example is just to verify some of my speculations.
Summary
- What is a delegate?
- A delegate loosely resembles a function pointer in C language;
- A delegate is a type that represents references to methods with a particular parameter list and return type in C#;
- In order to assign a method to a delegate instance, the types of the parameter list and the return type of the method need to exactly match the delegate definition;
- If the method is assigned to a delegate instance, it can be called/invoked through the delegate instance.
- Can we define delegates with generic types?
- Yes, we can define delegates with a generic parameter list and a generic return type.
- Where can we define delegates in a C# program?
- A delegate is a type in C#;
- A delegate can be defined at the same level as the classes;
- A delegate can be defined inside a class;
- A delegate cannot be defined inside a method.
- What is the
Target
property of a delegate instance?- The
Target
property of a delegate instance is the class instance on which the current delegate invokes the instance method; - When a
static
method is assigned to a delegate instance, theTarget
property isnull
; - When an instance method is assigned to a delegate instance, the
Target
property is "generally" the class instance; - If the delegate instance invokes one or more instance methods, this property returns the target of the last instance method in the invocation list.
- The
- What is
Func<T, TResult>
?- The
Func<T, TResult>
is a pre-defined generic delegate type by Microsoft in theSystem
namespace; - It has exactly one input parameter and a return value;
- The
T
is the type of the input parameter; - The
TResult
is the type of the return value.
- The
- What is
Action<T>
?- The
Action<T>
is a pre-defined generic delegate type by Microsoft in theSystem
namespace; - It has exactly one input parameter and does not return a value;
- The
T
is the type of the input parameter.
- The
- What is an event?
- An event is a special kind of instance of a delegate;
- Events enable a class or object to notify other classes or objects when something of interest occurs. The class that sends (or raises) the event is called the publisher and the classes that receive (or handle) the event are called subscribers;
- The publisher determines when an event is raised;
- The subscribers determine what action is taken in response to the event;
- An event can have multiple subscribers;
- A subscriber can handle multiple events from multiple publishers;
- Events that have no subscribers are never raised;
- Events are typically used to signal user actions such as button clicks or menu selections in graphical user interfaces;
- When an event has multiple subscribers, the event handlers are invoked synchronously when an event is raised.
- What is an anonymous method?
- An anonymous function/method is an
inline
statement or expression that can be used wherever a delegate type is expected; - You can use it to initialize a named delegate or pass it instead of a named delegate type as a method parameter;
- You define an anonymous method by the
delegate
syntax (see example 8).
- An anonymous function/method is an
- What is a lambda expression?
- A lambda expression is an anonymous method;
- A lambda expression style anonymous method is defined by
lambda
syntax (see example 9).
- What is a closure? Does C# have closures?
- Closures are well known subjects in functional programming. Unfortunately, I did not find an official documentation from Microsoft regarding to closures in C#;
- Borrow the definition from Mozilla for Javascript - "Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure 'remembers' the environment in which it was created". I hope this definition to be clear enough and you can understand it;
- In simpler terms, we can describe the closure behavior in the following sentences:
- We have a method that returns a delegate instance referencing an anonymous function/lambda expression defined in this method. For simplicity, let us call the method
M
and the anonymous functionA
; - The method
M
declared some local variables; - The anonymous function
A
referenced the local variables in a lexical(static) scope; - We can obtain a delegate instance to the anonymous function
A
by calling the methodM
. For simplicity, let us call the delegate instanceD
; - We may think that the local variables declared in "M" would have been cleared/destroyed after
M
returns. But when we invoke the delegate instanceD
, we will find thatD
"remembers" the local variables declared inM
. - The behavior that anonymous functions/lambda expressions "remember" the local variables in a lexical(static) scope is called a closure behavior;
- If we call the method
M
multiple times, each of the delegate instances returned byM
remembers an independent set of local variables.
- We have a method that returns a delegate instance referencing an anonymous function/lambda expression defined in this method. For simplicity, let us call the method
- Yes, C# has closures. You can take a look at the example 10 in this note.
- How can C# delegate instances "remember" the local variables of the method that creates them in-line?
- Unfortunately, I did not find an official documentation from Microsoft regarding to the implementation details on how C# delegate instances remember the local variables;
- In the example 11, by checking the
Target
property of the delegate instance, I find that theTarget
instance is not the class instance that we use to get the delegate instance; - I have the impression that the "information" regarding to the local variables should be stored in the
Delegate_Func_Action_M.Examples.AnIntanceClass4ClosureTarget+<>c__DisplayClass1
. But I need Microsoft to confirm it; - I hope that Microsoft can release some details on how C# closures "remember" the local variables.
- Does a delegate instance have any impact on garbage collection?
- Yes, delegate instances have an impact on garbage collection;
- If we assign an instance method to a delegate instance, the delegate instance holds a reference to the class instance. As long as the delegate instance is still accessible, the class instance should not be garbage collected.
Points of Interest
- This is a note of loosely coupled 11 small examples on the general subject of delegates, events, anonymous methods, and closures;
- I could not find the official documentation on closures from Microsoft, so this note has some of my own speculations. I hope that Microsoft can release some official documentation on closures in C# on MSDN, so we do not need to rely on the un-official on-line blogs;
- I hope you like my posts and I hope this note can help you in one way or the other.
History
- 19th February, 2016: Initial revision