public static void Main() { TestClass test = new TestClass(); // Executing a method directly is pretty straight forward. // What if we don't want to execute these methods now, // but want to execute them when some event occurs? // This is where delegates come in. ImSpeakingNow("How are you?"); MathMethods.MathSpeaks("I'm doing fine"); // How did we get from methods to delegates to lambdas? How are they related? // We use delegates to reference any method that has a specific signature. // As long as the signatures match, we can reference any method // and execute it using the delegate. // Once upon a time you would create a delegate using object constructor syntax. // This creates a reference to a method, which can be executed at any time. SpeakDelegate me = new SpeakDelegate(ImSpeakingNow); SpeakDelegate math = new SpeakDelegate(MathMethods.MathSpeaks); Action <string, int> xxx = MathMethods.NewMethod; xxx("hello", 9); Action <string, int[]> newAction = MathMethods.NewMethod2; int[] myints = new int[] { 9, 8, 7 }; newAction("I an another action", myints); Func <string, int, int> myFunc = MathMethods.myFunc; myFunc += MathMethods.myFunc2; int length = myFunc("Paul", 60); // Now execute the method you're referencing using the delegate it's mapped to. me("What a sunny day"); math("I like to count"); // Using the object constructor syntax was a little cumbersome, so // "implied conversion" was introduced. The compiler knows that // the method "TalkingTest" has the same signature as the SpeakDelegate // so it performs all the heavy lifting under the covers and allows // you to simply assign a method to a delegate. SpeakDelegate abc = test.TalkingTest; abc("I'm new"); me = test.TalkingTest; // A Multicast Delegate is a delegate that holds the references of more than one function. // When a multicast delegate is executed, then all the functions which are referenced by // the delegate are going to be executed. me += ImSpeakingNow; me += MathMethods.MathSpeaks; // Notice that all 3 methods that this deletate references are executed with one line of code. // Also notice that all 3 methods are called synchronously! me("We're speaking the same language"); // Example of passing a delegate as a parameter to a method ILikeDelegates(me, "All my delegates should say this"); ILikeDelegates(ImSpeakingNow, "All my delegates should say this"); // We can remove method references from the delegate to have as few or as many // references in the delegate that we want. me -= ImSpeakingNow; me -= MathMethods.MathSpeaks; me("Just me now"); // Here are a couple more examples of using delegates MathMethods.DoMathDelegate doMath = test.AddThese; int Total = doMath(4, 8); Console.WriteLine($"Total of 4+8 = {Total}"); // An "Action" is a predefined delegate that takes 0 or more parameters, does SOMETHING and returns void. // An Action can take no parameter or Action someAction = test.DoSomething; someAction(); // Events help implement the "publisher/subscriber" model. // Any object can publish a set of events to which other objects can subscribe. // Let's say that we want to be notified whenever a method in the // TestClass class completes. That class has an event called OperationCompleteEvent // that is fired to tell anyone listening about that event. test.OperationCompleteEvent += OnOperationComplete; // Now that our event has been hooked up, let's execute the same // code as before, but this time the events will fire and we will // be notified by having our event handlers called. doMath(4, 8); someAction(); // Don't want to be notified of these events anymore test.OperationCompleteEvent -= OnOperationComplete; // There are many times when we want to execute code in some method // but it will only ever be called in one place. It seems like a // real waste to have to declare a method like we did // with "ImSpeakingNow(string SayThis)" just for that purpose. // To that end, the "Anonymous" method was created. // Anonymous methods provide a way to write unnamed inline // statement blocks that can be executed in a delegate invocation. List <String> names = new List <String>(); names.Add("Fred"); names.Add("Sam"); names.Add("Bob"); // The following demonstrates the anonymous method feature of C# // to display the contents of the list to the console names.ForEach(delegate(String name) { Console.WriteLine(name); }); me = delegate(string Something) { Console.WriteLine($"Anonymous says: {Something}"); }; me("I am here!"); // A lambda expression is nothing more than syntactic sugar for an anonymous method. // The following lambda expression is EXACTLY the same as the anonymous method above. // The type of the parameter "Something" is inferred by the compiler. me = (Something) => { Console.WriteLine($"Lambda says: {Something}"); }; me("I am here!"); Func <int, int, int> ReturnSomething = (x, y) => { return(x + y); }; int value = ReturnSomething(9, 8); Console.WriteLine($"Value is {value}"); // The signature of the method called is: // public static int Calculate(DoMathDelegate DoMath, int first, int second) // // The first parameter is a lambda expression matching the delegate signature: // public delegate int DoMathDelegate(int first, int second) // // The next 2 parameters are the values consumed by the DoMathDelegate Console.WriteLine($"Value is {MathMethods.Calculate((a, b) => a + b, 1, 2)} using lambda"); Console.WriteLine($"Value is {MathMethods.Calculate((x, z) => x * z, 1, 2)}"); Console.WriteLine($"Value is {MathMethods.Calculate((q, r) => q - r, 1, 2)}"); Console.WriteLine($"Value is {MathMethods.Calculate((f, h) => f / h, 1, 2)}"); // Parameter delegates are often designed to work on data that is internal to the class/type. // The delegate is typically used to iterate over the internal data values to // produce some kind of result or filter the data in some way. MathMethods.AppendValue(2); MathMethods.AppendValue(3); MathMethods.AppendValue(4); MathMethods.AppendValue(5); MathMethods.AppendValue(6); MathMethods.AppendValue(7); MathMethods.AppendValue(8); Console.WriteLine($"CalculateTotal addition is {MathMethods.CalculateTotal((a, b) => a + b)}"); Console.WriteLine($"CalculateTotal multiplication is {MathMethods.CalculateTotal((a, b) => a * b)}"); // Here we will create a lambda expression that will be used to filter out all even numbers List <int> even = MathMethods.RunFilter(i => i % 2 == 0); foreach (int x in even) { Console.WriteLine($"Even {x}"); } // Here we will create a lambda expression that will be used to filter out all odd numbers List <int> odd = MathMethods.RunFilter(i => i % 2 == 1); foreach (int x in odd) { Console.WriteLine($"Odd {x}"); } /// A Predicate is a delegate like the Func and Action delegates. /// It represents a method that checks whether the passed parameter meets a set of criteria. /// A predicate delegate methods must take one input parameter and return a boolean - true or false. /// You'll find that built in delegate types like "Action", "Func<>" and "Predicate<>" can be used /// instead of creating your own custom delegates most of the time. Here's an example of using /// a built-in "Predicate<int>" instead of custom "FilterDelegate". List <int> lessThan5 = MathMethods.RunFilterPredicate(i => i < 5); Console.WriteLine($"Values less than 5 using predicate"); foreach (int x in lessThan5) { Console.WriteLine($"{x}"); } //----------------- What's happening under the hood? Expression Trees! System.Linq.Expressions.Expression <Func <int, int> > myExpression = x => x * x; string lambdaString = myExpression.ToString(); Func <int, int> compiledDelegate = myExpression.Compile(); int parameter = 8; int answer = compiledDelegate(parameter); Console.WriteLine($"Result of calling '{lambdaString}' using parameter '{parameter}' is '{answer}'"); myExpression.DumpExpression(); Expression <Func <int, bool> > expr = i => i % 2 == 0; expr.DumpExpression(); Expression <Func <string, string, string> > tree = (a, b) => a.ToLower() + b.ToUpper(); tree.DumpExpression(); Expression <SpeakDelegate> myDelegate = (sayThis) => Console.WriteLine(sayThis); myDelegate.DumpExpression(); FilmCritic.DemonstrateDeferredExecution("Rambo", "First", new DateTime(2009, 1, 1)); List <string> listOfNames = new List <string>() { "John Doe", "Jane Doe", "Jenna Doe", "Joe Doe" }; // Query syntax IEnumerable <string> qNames = from name in listOfNames where name.Length <= 8 select name; // Method syntax var mNames = listOfNames.Where(name => name.Length <= 8); // Representation of the query Expression <Func <IEnumerable <string>, IEnumerable <string> > > lambda = (myList) => from name in myList where name.Length <= 8 select name; lambda.DumpExpression(); Console.WriteLine($"{lambda}"); // Compile and Execute the query var compiledLinq = lambda.Compile(); IEnumerable <string> expressionNames = compiledLinq(listOfNames); foreach (string x in expressionNames) { Console.WriteLine($"{x}"); } }