Skip to content

Instantly share code, notes, and snippets.

@asarnaout
Created March 11, 2018 14:35
Show Gist options
  • Save asarnaout/d99951cae0f49ebf3d69c2b2ac3e331c to your computer and use it in GitHub Desktop.
Save asarnaout/d99951cae0f49ebf3d69c2b2ac3e331c to your computer and use it in GitHub Desktop.
Delegates and Events Tutorial in C#
using System;
namespace Delegates.Tutorial
{
public class DelegatesTutorial
{
public delegate void TestDelegate(int x, bool y);
// A delegate declaration gets translated to a class of the same name by the compiler, this class inherits from the
// MulticastDelegate base class. The MulticastDelegate holds an InvocationList for each instance of the delegate, which is a
// list of all the methods called via the delegate in the same order as they are called
/// <summary>
/// Using a delegate allows the programmer to encapsulate a reference to a method inside a delegate object. The delegate
/// object can then be passed to code which can call the referenced method, without having to know at compile time which
/// method will be invoked.
/// </summary>
public static void DoSomeWorkWithDelegates()
{
// Defining delegates and how to use them
TestDelegate del1 = new TestDelegate(TestHandler1);
//Creating an instance of a delegate means that you're creating an object holding a reference to some method having
//the same parameters and return type as the delegate
TestDelegate del2 = new TestDelegate(TestHandler2);
del1 += del2; // Call del2 after del1 with the same parameters, thus this delegate's InvokationList will hold TestHandler1 followed by TestHandler2
var invocationList = del1.GetInvocationList();
TestDelegate del3 = new TestDelegate(TestHandler3);
TestDelegate del4 = new TestDelegate((x, y) =>
{
Console.WriteLine("Anonymous Lambda Function Called with params: " + x + " and " + y);
Console.WriteLine("Anonymous Lambda Function Called at : " + DateTime.UtcNow);
});
//Lambdas are by definition anonymous methods, and thus the delegate could be a pointer to such methods
//The compiler automatically detects the types of the parameters x and y, thus you need not to specify their types
TestDelegate del5 = delegate (int x, bool y)
{
Console.WriteLine("Anonymous Lambda Function Called with params: " + x + " and " + y);
Console.WriteLine("Anonymous Lambda Function Called at : " + DateTime.UtcNow);
}; // Equivalent to lambdas
var date = DateTime.UtcNow.Minute;
if (date % 2 == 0)
DoWork(DateTime.UtcNow, del1);
else
{
if (date % 3 == 0)
DoWork(DateTime.UtcNow.AddDays(-1), del3);
else
DoWork(DateTime.UtcNow.AddDays(-2), del4 + del5);
}
//Using delegates, you could call a function that performs multiple tasks before invoking the delegate, and
//thus it would save you the burden of rewriting the code multiple times where each time a different method
//is called, so in the above example, if delegates weren't used, the same DoWork function would have been
//replacated two times where each time it calls a different handler
}
private static void DoWork(DateTime date, TestDelegate del)
{
Console.WriteLine("Pre-Invocation");
if (date == DateTime.UtcNow)
{
del(1, true); //Invoking the delegate
}
else if (date == DateTime.UtcNow.AddDays(-1))
{
del(2, true);
}
else
{
del(3, false);
}
}
private static void TestHandler1(int x, bool y)
{
if (y)
Console.WriteLine("Handler 1 Invoked with true value " + x);
else
Console.WriteLine("Handler 1 Invoked with false value " + x);
}
private static void TestHandler2(int x, bool y)
{
if (y)
Console.WriteLine("Handler 2 Invoked with true value " + x);
else
Console.WriteLine("Handler 2 Invoked with false value " + x);
}
private static void TestHandler3(int x, bool y)
{
if (y)
Console.WriteLine("Handler 3 Invoked with true value " + x);
else
Console.WriteLine("Handler 3 Invoked with false value " + x);
}
}
}
using System;
namespace Delegates.Tutorial
{
public class EventsTutorial
{
public static void DoSomeWorkWithEvents()
{
var myType = new MyType();
if (DateTime.Now.Minute % 2 == 0)
{
myType.OperationStarted += HandleEventStart1; //Subscribe the event to a handler
myType.OperationStarted += HandleEventStart2;
}
else
{
myType.OperationStarted += HandleEventStart1;
}
myType.OperationEnded += HandleEventEnd;
myType.PerformOperation(1, true);
}
private static void HandleEventStart1(object sender, OperationEventArgs args)
{
if (args.Value)
Console.WriteLine("Operation 1 started with a true value given the number: " + args.Number);
else
Console.WriteLine("Operation 1 started with a false value given the number: " + args.Number);
}
private static void HandleEventStart2(object sender, OperationEventArgs args)
{
if (args.Value)
Console.WriteLine("Operation 2 started with a true value given the number: " + args.Number);
else
Console.WriteLine("Operation 2 started with a false value given the number: " + args.Number);
}
private static void HandleEventEnd(object sender, EventArgs args)
{
Console.WriteLine("Operation on the " + sender.GetType().Name + " type has been completed");
}
}
}
using System;
namespace Delegates.Tutorial
{
public class MyType
{
public delegate void OperationDelegate(object sender, OperationEventArgs args);
public event OperationDelegate OperationStarted;
//An event cannot be defined by its own, it has to have a delegate which will connect it to the event handler, different
//invocators of the type will want to have different handlers to the event and thus events are tied to delegates
//EventArgs is a wrapper which holds a set of arguments related to this event
//All event handlers have the signature which takes the sender and the event args as parameters, and thus events
//are always linked to delegates which will connect them to their respective event handlers.
//The declaration below is a short-hand which will internally create a similar delegate and event as declared above
//public EventHandler<OperationEventArgs> OperationStarted;
public event EventHandler OperationEnded;
// The EventHandler delegate should be used when the event handler uses no event data
public void PerformOperation(int num, bool val)
{
OnOperationStarted(num, val);
Console.WriteLine("Performing Operation");
OnOperationEnded();
}
protected virtual void OnOperationStarted(int num, bool val) //Allow sub classes to re-implement this function
{
if (OperationStarted != null) // This checks if the event has been subscribed to an event handler or not
{
OperationStarted(this, new OperationEventArgs{ Number = num, Value = val});
}
}
protected virtual void OnOperationEnded()
{
if (OperationEnded != null)
{
OperationEnded(this, EventArgs.Empty);
}
}
}
}
using System;
namespace Delegates.Tutorial
{
public class OperationEventArgs : EventArgs
{
public int Number { get; set; }
public bool Value { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Delegates.Tutorial
{
public class FunctionsActionsTutorial
{
public static void DoSomeWorkWithFuncsAndActions()
{
//Functions and actions are delegates that are built in to the .NET framework to save you the burden of defining your custom delegates
Func<int, int, bool> func;
// A function is a delegate to a method which returns a value
if (DateTime.UtcNow.Minute % 2 == 0)
{
func = (x, y) => x % 2 == y;
}
else
{
func = FunctionMethod;
}
Console.WriteLine(func(4, 1));
Action<int, string> action;
// An action is a delegate to a void method
if (DateTime.UtcNow.Minute % 2 == 0)
{
action = (x, y) => Console.WriteLine("Write " + (x / 2) + " " + y);
}
else
{
action = ActionMethod;
}
action(10, "Test"); // or action.Invoke(10, "Test");
//Filtering collections using functions
Func<int, bool> func2 = new Func<int, bool>(x => x % 2 == 0);
List<int> list = new List<int> { 3, 10, 30, 5, 20 };
list = list.Where(func2).ToList(); // Invoking the function to filter the list
IEnumerable<dynamic> list2 = list.Select(ProjectionFunc);
//A predicate is a special func which by default returns a bool
Predicate<string> pred = x => x.Length == 2;
var lst = new List<string>() {"", "12", "21", "0"}.Where(x => pred(x));
foreach (var item in lst)
{
Console.WriteLine(item);
}
}
private static dynamic ProjectionFunc(int x)
{
return new
{
Original = x,
Increment = x + 1
};
}
private static bool FunctionMethod(int x, int y)
{
return x % 3 == y;
}
private static void ActionMethod(int x, string y)
{
Console.WriteLine("Action Invoked at " + DateTime.UtcNow);
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
namespace Delegates.Tutorial
{
public class Program
{
public static void Main(string[] args)
{
DelegatesTutorial.DoSomeWorkWithDelegates();
EventsTutorial.DoSomeWorkWithEvents();
FunctionsActionsTutorial.DoSomeWorkWithFuncsAndActions();
Console.ReadLine();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment