COMPSCI 280 S2 2015 Enterprise Software Development Delegates and lambda functions Jim Warren, jim@cs.auckland.ac.nz Today’s learning objectives To understand and be able to apply C# language features in a variety of contexts for 2 Development of delegates Use of lambda functions COMPSCI 280 Delegates Delegates are object-oriented, type-safe, and secure function pointers A delegate is a reference type that defines a method signature A delegate instance holds one or more methods Methods can be static or non-static Delegates allow methods to be passed as parameters. Delegates can be used to define callback methods. BOSS Job Finished??? Finished??? Call back when Finished 3 Delegates can be chained together; for example, multiple methods can be called on a single event. It is a foundation of event handling. COMPSCI280 Creating Delegates The type of a delegate is defined by the name of the delegate public delegate void Print(string s); Each delegate is limited to referencing methods of a particular kind only. The type is indicated by the delegate declaration – the input parameters and return type Example: Two methods with the same signature as the delegate declared above public static void printToLower (string s) { Console.WriteLine("static: " + s.ToLower()); } public void printToUpper (string s) { Console.WriteLine("instance: " + s.ToUpper()); } Static method Creating a New Delegate Object Use the new operator The argument is the method call, but without the arguments to the method Delegate objects are immutable. (can’t change) A delegate can reference static or instance method Print v1 = new Print(printToLower); Print v2 = new Print(new Program().printToUpper ); 4 COMPSCI280 Using Delegates Once a delegate is instantiated, a method call made to the delegate will be passed by the delegate to that method and will call the underlying method Use the name of the delegate, followed by the parenthesized arguments to be passed to the delegate Static method output: static: this is a test v1("This is a test"); v2("This is a test"); 5 COMPSCI280 output: instance: THIS IS A TEST Using Arrays Create an array of delegate objects Instantiate each delegate object with various instance methods defined above Note: In C#, if we reference a method on an object (omitting the parentheses), C# instead treats the method name like a field, returning the object representing that method. Call each delegate object by using a loop -> invoke the underlying method public delegate void Print(string s); Print[] arr = new Print[2]; arr[0] = new Print(printToLower); arr[1] = new Print(new Program().printToUpper); foreach (Print p in arr) p("In an array"); static: in an array instance: IN AN ARRAY 6 COMPSCI280 Named & Anonymous methods Named Method public delegate void Print(string s); A delegate can be associated with a named method. When you instantiate a delegate using a named method, the method is passed as a parameter The method that you pass as a delegate parameter must have the same signature as the delegate declaration. A delegate instance may encapsulate either a static or an instance method. Named method Print v1 v1("This Print v2 v2("This = printToLower; is another test"); = new Program().printToUpper; is another test"); Anonymous Method In C# 2.0 (from 2005), you can declare a delegate using an anonymous method Print a1 = delegate(string j){ Anonymous method 7 Console.WriteLine("Anonymous:" + j); }; a1("Well done"); COMPSCI280 Multicast delegates Delegate objects can be assigned to one delegate instance to be multicast using the + operator. A delegate can simultaneously reference multiple methods, and it will invoke all underlying methods Conditions: Methods are invoked sequentially, in the order added. Only delegates of the same type can be composed. The - operator can be used to remove a component delegate from a composed delegate. public delegate void Print(string s); A delegate object Print mp2 = null; mp2 += printToLower; mp2 += new Program().printToUpper; mp2("Hello World"); Output: static: hello world Instance: HELLO WORLD 8 COMPSCI280 Event Event Basic: An event is a message sent by an object to signal the occurrence of an action. The action could be caused by user interaction, such as a mouse click, or it could be triggered by some other program logic. The object that raises the event is called the event sender. The object that captures the event and responds to it is called the event receiver. The event sender class doesn’t know which object or method will receive (handle) the events it raises. What is needed is an intermediary (or pointerlike mechanism, delegate) between the source and the receiver. An event is a member of a delegate type that enables an object or class to provide notifications. Delegate Sender 9 COMPSCI280 Receiver Example: Declare a delegate that takes two parameters: the source that raised the event, and the data for the event public delegate void EventHandler(object sender, System.EventArgs e); In the Button class (e.g. in a Windows Forms Application) Defines a click event of type EventHandler Inside the Button class, the click member is exactly like a field of type EventHandler Outside the Button class, the click member can only be used on the lefthand side of the += and -= operators. 10 The += operator adds a handler for the event and -= removes a handler for the event public class Button { public event EventHandler Click; public void Reset() { Click = null; } … COMPSCI280 Example: In the Form1 class (Windows application) Create a button object Create a event handler method Connect the event handler method to the click event of the button object Event handler method A lot of this is set up for you when your create a new Windows Forms Application in VS and drag a button onto it. public Form1() { ... button1.Click += new EventHandler(button1_Click); } public void button1_Click(object sender, EventArgs e){ Console.WriteLine("Clicked"); } You can also reuse the same method for multiple events button2.Click += new EventHandler(button1_Click); button3.Click += new EventHandler(button1_Click); 11 COMPSCI280 Lambda expressions A lambda expression is An anonymous function used to create delegates or expression tree* types Denoted with => Can pronounce as ‘maps to’, ‘such that’ or ‘for which’ Same order of operations precedence as assignment with = Lambda expressions let you write local functions that can be passed as arguments or returned as the value of function calls Borrowing liberally from http://msdn.microsoft.com/enus/library/bb397687.aspx Particularly helpful for writing LINQ query expressions You can assign a lambda expression to a delegate type * See next slide 12 COMPSCI 280 Expression trees Holding an expression in a dynamic structure For C# this is as a nested series of objects E.g. a binary operator like + can have Left and Right terms, each of which themselves might be variables, constants or other operators A lambda expression is just ‘syntactic sugar’ It’s an easier way to write stuff that the (a+b)*c+7 compiler turns into delegate definitions http://commons.wikimedia.org/wiki/File:Exptree-ex-11.svg or expression trees Great further explanation at https://www.youtube.com/watch?v=P60pt5xlms0 and https://www.youtube.com/watch?v=0YJECE45jhk 13 Author Jamie King almost sounds drunk, but I think that’s just his natural speaking style COMPSCI 280 Syntactic sugar So as Jamie King illustrates on YouTube Func<int, bool> test= i => i > 5; Is equivalent to static bool qwerty(int i) { return i > 5; } Func<int,bool> test=qwerty; Either way, you can do 14 and then Console.Writeline(test(3)); Console.Writeline(test(8)); False True COMPSCI 280 Handout 07 Definition Expression lambda In general looks like (x, y) => x==y When there’s only one parameter you can skip the parentheses Statement lambda Statements in curly braces for the right-hand side of the lambda The above example is a function of type void, but you could include one or more return statements in the code, too 15 E.g. to perform a logical test for a bool delegate or compute a number for an int delegate COMPSCI 280 Use in queries Very convenient in interaction with LINQ Using an array as the data source for the query in this case The type of the first input (e.g. the ‘n’ above) is inferred to be the type of the elements in the source sequence So if we are using a database connection we may have the result of referencing a table or from a query, such as IEnumerable<Customer> (iterator on a set of Customer instances) And then we can do a query like: customers.Where(c => c.City == "London"); 16 Read “Customers, c, such that c’s city is London” In this case the ‘c’ has access to the Customer object’s properties (e.g. the City property, which might’ve been sourced from a field of that name in a customer table under MySQL) COMPSCI 280 Conclusion The ability to have variables reference functions is powerful Allows us to achieve a high degree of modularity between a ‘supervisor’ procedure and the procedures delegated to do subtasks Provides a powerful framework for event handling Lambda expressions are a convenient syntax for plugging anonymous functions into your code 17 Useful for many things, including responding to user actions in a Graphical User Interface (GUI) Useful for many things including implementing query criteria Keep (or get!) going with assignment 2 Next – we’ll look at further useful VS and C# features COMPSCI 280