Example #1
0
    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}");
        }
    }