Evolution of C# delegates

28
Marko Barić Senior software engineer @ Siemens CVC [email protected] Evolution of C# delegates

description

Evolution of C# delegates - overview of delegates in C#, from basic delegates in C# 1.0 to lambda expressions on C# 4.0

Transcript of Evolution of C# delegates

Page 1: Evolution of C# delegates

Marko BarićSenior software engineer @ Siemens [email protected]

Evolution of C# delegates

Page 2: Evolution of C# delegates

Agenda

• Basic delegates• Delegate types, delegate instances

• Combining multiple delegates• Method group conversions• Covariance and contravariance• Inline delegate actions• Captured variables (closures)• Lambda expressions

Page 3: Evolution of C# delegates

• Provide a level of indirection• Sort of "sigle method interface"• Delegates inhert from

System.Delegate

• To create and use delegates:• The delegate type needs to be declared• The code to be executed must be contained

in a method• A delegate instance must be created• The delegate instance must be invoked

What is a delegate?

Page 4: Evolution of C# delegates

delegate void StringProcessor(string input);

Declaring delegate types

keyword returntype

delegate type name arguments

Page 5: Evolution of C# delegates

class MyClass{ public void SomeMethod(string input) public static void SomeMethodStatic(string input)}

StringProcessor p1 = new StringProcessor(myClassInstance.SomeMethod);

StringProcessor p2 = new StringProcessor(MyClass.SomeMethod);

Creating delegate instances

delegate type

delegate instance variable

action to invoke on MyClass instance

static action to invoke on MyClass

Page 6: Evolution of C# delegates

StringProcessor p1 = new StringProcessor(myClassInstance.SomeMethod);

p1.Invoke("Some string");

// ...or shorter version p1("Some string");

Invoking delegates

p1("Some string");

p1.Invoke("Some string")

SomeMethod("Some string")

• Delegates can be treated like any other type• They have methods, multiple instance can be created...

Page 7: Evolution of C# delegates

delegate void StringProcessor(string input);class Person{ private string name; public Person(string name) { this.name = name; } public void SayTheMessage(string message) { Console.WriteLine("{0} says: {1}", name, message); } public static void SayTheMessageStatic(string message) { Console.WriteLine(message); } }

static void Main(string[] args){ Person john = new Person("John"); Person tom = new Person("Tom"); StringProcessor johnsVoice = new StringProcessor(john.SayTheMessage); StringProcessor tomsVoice = new StringProcessor(tom.SayTheMessage); StringProcessor someonesVoice = new StringProcessor(Person.SayTheMessageStatic);

johnsVoice("Hello!"); tomsVoice.Invoke("Hello!"); someonesVoice("Just saying something...");}

Example

// Result: "John says: Hello!""Tom says: Hello!""Just saying something..."

Page 8: Evolution of C# delegates

• All delegates inherit methods from System.Delegate:• Delegate.Combine()• Delegate.Remove()

• Every delegate has an invocation list - a list of actions to invoke

• "+", "+=", "-", "-="• When delegate is invoked, all

actions in the invocation list are invoked in the order they were added

Combining the delegates

Page 9: Evolution of C# delegates

Person john = new Person("John");Person tom = new Person("Tom");Person mike = new Person("Mike");

StringProcessor johnsVoice = new StringProcessor(john.SayTheMessage);StringProcessor tomsVoice = new StringProcessor(tom.SayTheMessage);StringProcessor mikesVoice = new StringProcessor(mike.SayTheMessage);

StringProcessor twoCombined = johnsVoice + tomsVoice;StringProcessor allCombined = twoCombined + mikesVoice;allCombined += new StringProcessor(john.SayTheMessage);

allCombined("What's up!");

// Result: "John says: What's up!""Tom says: What's up!""Mike says: What's up!""John says: What's up!"

Example of combining delegates

Page 10: Evolution of C# delegates

//Delegate typedelegate void KeyPressEventHandler(object sender, KeyPressEventArgs e);

//Target actionvoid LogKeyEvent(object sender, KeyPressEventArgs e){ /* do something */ }

void LogKeyEvent(int x){ /* do something */ }

button.KeyPress += new KeyPressEventHandler(LogKeyEvent);

button.KeyPress += LogKeyEvent;

Method group converions

Method group is converted to compatible delegate type

Page 11: Evolution of C# delegates

Click -----> void EventHandler(object sender, EventArgs e)KeyPress -----> void KeyPressEventHandler(object sender, KeyPressEventArgs e)MouseClick ---> void MouseClickEventHandler(object sender, MouseEventArgs e)

Button button = new Button();button.Text = "Click me";button.Click += LogPlainEvent;button.KeyPress += LogPlainEvent;button.MouseClick += LogPlainEvent;

Form form = new Form();form.Controls.Add(button);Application.Run(form);

static void LogEvent(object sender, EventArgs e){ Console.WriteLine("An event occurred");}

Contravariance of delegates

Same action for 3 different delegates!

Page 12: Evolution of C# delegates

delegate Stream MyDelegate();

static MemoryStream GenerateSampleData(){ byte[] buffer = new byte[16]; return new MemoryStream(buffer);}

MyDelegate d = GenerateSampleData();Stream stream = d();MemoryStream stream = d();

Covariance of delegates

Stream

MemoryStream

Page 13: Evolution of C# delegates

Built-in delegate types in .NET

delegate void Action<T>(T arg);delegate void Action<T1, T2>(T1 arg1, T2 arg2);...

delegate TResult Func<T, TResult>(T arg);delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);...

delegate bool Predicate<T>(T arg);...

Page 14: Evolution of C# delegates

• You don't need action method to exist - you can create it inline

Inline delegate actions

Action<int> printRoot = delegate(int number){ Console.WriteLine(Math.Sqrt(number));};

printRoot(9);

Func<string, int> getLength = delegate(string input){ return input.Length;};

int length = getLength("Some string");

Invoke it like other delegates

Page 15: Evolution of C# delegates

button.Click += delegate(object sender, EventArgs e) { ... };

Button button = new Button();button.Text = "Click me";button.Click += delegate { Console.WriteLine("Click"); };button.KeyPress += delegate { Console.WriteLine("KeyPress"); };button.MouseClick += delegate { Console.WriteLine("MouseClick"); };

Ingnoring inline delegate arguments

• When you won't use delegate arguments you can loose them in definition

Page 16: Evolution of C# delegates

• Beware of the compiler limitations:

Ingnoring inline delegate arguments

// Thread class has several different constructorspublic Thread(ThreadStart start)public Thread(ParameterizedThreadStart start)

// There are 2 types of delegates involvedpublic delegate void ThreadStart()public delegate void ParameterizedThreadStart(object obj)

new Thread(delegate() { Console.WriteLine("Something..."); } );new Thread(delegate(object o) { Console.WriteLine("Something..."); } );new Thread(delegate { Console.WriteLine("Something..."); } );

Page 17: Evolution of C# delegates

Captured variables (closures)

• Captured variables are outer variables used (captured) in the scope of anonymous methodvoid EnclosingMethod(){ string outerVariable = "Default string";

Action<int> a = delegate() { string localVariable = outerVariable; };

a();}

Page 18: Evolution of C# delegates

delegate void MyDelegate();

string captured = "before x is created";MyDelegate x = delegate{ Console.WriteLine(captured); captured = "changed by x";};captured = "before x is invoked";x();Console.WriteLine(captured);captured = "before second invocation";x();

// Result:"before x is invoked""changed by x""before second invocation"

Captured variables

The captured variable is the same one that the outer code uses!!!

Page 19: Evolution of C# delegates

• A captured variable lives for at least as long as any delegate instance referring to it

Lifetime of captured variables

public MethodInvoker CreateDelegateInstance(){ int counter = 5; MethodInvoker increment = delegate { Console.WriteLine(counter); counter++; };

increment(); return counter;}...

MethodInvoker x = CreateDelegateInstance();x();x();

Page 20: Evolution of C# delegates

MethodInvoker[] invokers = new MethodInvoker[2];

int outsideVariable = 0;for (int i=0; i<2; i++){ int insideVariable = 0; invokers[i] = delegate { Console.WriteLine ("({0},{1})", outsideVariable, insideVariable); outsideVariable++; insideVariable++; };}MethodInvoker first = invokers[0];MethodInvoker second = invokers[1];

first();first();first();second();second();

Things can get tricky very fast

// Result:(0,0)(1,1)(2,2)(3,0)(4,1)

Page 21: Evolution of C# delegates

• They are evolution of anonymous methods

• More readable and compact than other delegate forms

• Many shortcuts and "syntatic sugar" tricks allow most compat form of code

• Brings new operator "=>" (spelled as "goes to")

Lambda expressions

Page 22: Evolution of C# delegates

delegate TResult Func<T, TResult>(T input);

Func<string, int> returnLength;returnLength = delegate (string text) { return text.Length; };

returnLength = (string text) => { return text.Length; };

Simple lambda expression

input arguments statements

(list of input arguments) => { statements }

Page 23: Evolution of C# delegates

returnLength = (string text) => { return text.Length; }

returnLength = (string text) => text.Length

returnLength = (text) => text.Length

returnLength = text => text.Length

Shortening the lambdas

If the statement is single expression, you can loose the braces, return statement and semicolon

Compiler can guess type of input arguments,so you can loose it

If there is single input argument, you can loose the parentheses

Page 24: Evolution of C# delegates

Let's recap

Func<string, int> returnLength = new Func<string, int>(GetLength);returnLength(text);

Func<string, int> returnLength = GetLength;returnLength(text);

returnLength = delegate (string text) { return text.Length; };

returnLength = (string text) => { return text.Length; };

returnLength = text => text.Length;

Page 25: Evolution of C# delegates

public static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> predicate){ if (source == null || predicate == null) { throw new ArgumentNullExcpetion(); }

foreach (T item in source) { if (predicate(item)) { yield return item; } } }

// Usage:var items = new List<string> { "John", "Tom", "Mike" };var filteredItems = items.Where(i => i.StartsWith("J"));

Real-life lamba example - Where

Page 26: Evolution of C# delegates

Real-life lambdas in action

Page 27: Evolution of C# delegates

Q & A?

Page 28: Evolution of C# delegates

Thank you!