Evolution of C# delegates

Post on 10-May-2015

518 views 2 download

Tags:

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

Marko BarićSenior software engineer @ Siemens CVCmarko.baric@siemens.com

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

• 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?

delegate void StringProcessor(string input);

Declaring delegate types

keyword returntype

delegate type name arguments

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

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...

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..."

• 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

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

//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

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!

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

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);...

• 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

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

• 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..."); } );

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();}

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!!!

• 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();

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)

• 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

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 }

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

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;

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

Real-life lambdas in action

Q & A?

Thank you!