Created
March 11, 2018 14:35
-
-
Save asarnaout/d99951cae0f49ebf3d69c2b2ac3e331c to your computer and use it in GitHub Desktop.
Delegates and Events Tutorial in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace Delegates.Tutorial | |
{ | |
public class OperationEventArgs : EventArgs | |
{ | |
public int Number { get; set; } | |
public bool Value { get; set; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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