3. What is a delegate?
•
•
•
•
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
5. Creating delegate instances
class MyClass
{
public void SomeMethod(string input)
public static void SomeMethodStatic(string input)
}
StringProcessor p1 = new StringProcessor(myClassInstance.SomeMethod);
delegate instance
variable
delegate
type
action to invoke on
MyClass instance
StringProcessor p2 = new StringProcessor(MyClass.SomeMethod);
static action to
invoke on MyClass
6. Invoking delegates
StringProcessor p1 = new StringProcessor(myClassInstance.SomeMethod);
p1.Invoke("Some string");
// ...or shorter version
p1("Some string");
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...
7. Example
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");
// Result:
"John says: Hello!"
"Tom says: Hello!"
"Just saying something..."
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...");
}
8. Combining the 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
9. Example of combining 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!"
10. Method group converions
//Delegate type
delegate void KeyPressEventHandler(object sender, KeyPressEventArgs e);
//Target action
void LogKeyEvent(object sender, KeyPressEventArgs e)
{ /* do something */ }
void LogKeyEvent(int x)
{ /* do something */ }
button.KeyPress += new KeyPressEventHandler(LogKeyEvent);
button.KeyPress += LogKeyEvent;
Method group is converted to
compatible delegate type
11. Contravariance of 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;
Same action for 3 different delegates!
Form form = new Form();
form.Controls.Add(button);
Application.Run(form);
static void LogEvent(object sender, EventArgs e)
{
Console.WriteLine("An event occurred");
}
12. Covariance of delegates
delegate Stream MyDelegate();
Stream
static MemoryStream GenerateSampleData()
{
byte[] buffer = new byte[16];
return new MemoryStream(buffer);
}
MyDelegate d = GenerateSampleData();
Stream stream = d();
MemoryStream stream = d();
MemoryStream
14. Inline delegate actions
• You don't need action method to exist you can create it inline
Action<int> printRoot = delegate(int number)
{
Console.WriteLine(Math.Sqrt(number));
};
printRoot(9);
Invoke it like other delegates
Func<string, int> getLength = delegate(string input)
{
return input.Length;
};
int length = getLength("Some string");
15. Ingnoring inline delegate
arguments
• When you won't use delegate arguments
you can loose them in definition
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"); };
16. Ingnoring inline delegate
arguments
• Beware of the compiler limitations:
// Thread class has several different constructors
public Thread(ThreadStart start)
public Thread(ParameterizedThreadStart start)
// There are 2 types of delegates involved
public 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..."); } );
17. Captured variables (closures)
• Captured variables are outer variables
used (captured) in the scope of
anonymous method
void EnclosingMethod()
{
string outerVariable = "Default string";
Action<int> a = delegate()
{
string localVariable = outerVariable;
};
a();
}
18. Captured variables
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"
The captured variable is the same one
that the outer code uses!!!
19. Lifetime of captured variables
• A captured variable lives for at least as
long as any delegate instance referring to
it MethodInvoker CreateDelegateInstance()
public
{
int counter = 5;
MethodInvoker increment = delegate
{
Console.WriteLine(counter);
counter++;
};
increment();
return counter;
}
...
MethodInvoker x = CreateDelegateInstance();
x();
x();
20. Things can get tricky very fast
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];
// Result:
MethodInvoker second = invokers[1];
(0,0)
first();
first();
first();
second();
second();
(1,1)
(2,2)
(3,0)
(4,1)
21. Lambda expressions
• 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")
23. Shortening the lambdas
returnLength = (string text) => { return text.Length; }
If the statement is single expression,
you can loose the braces, return statement and semicolon
returnLength = (string text) => text.Length
Compiler can guess type of input arguments,
so you can loose it
returnLength = (text) => text.Length
If there is single input argument,
you can loose the parentheses
returnLength = text => text.Length
- Delegate instanca će onemogućiti da garbage collector očisti njen target dok kod je ona živa. Ovo može stvoriti neželjeni memory leak jer će long-lived objekt (u našem slučaju instanca delegata) držati na životu short-lived objekt (u našem slučaju target)