Upload
dinhthuy
View
224
Download
1
Embed Size (px)
Citation preview
Interrupting Program Flow and Handling Events
Binay DEMİR
Visual C# 2010 Microsoft
Interrupting Program Flow and Handling Events
Interrupting the current flow of execution and performing other sometimes is a very important . The application has to temporarily stop what it is doing and handle user input.
To handle this type of application, the runtime has to provide two things: a means of indicating that something urgent has happened and a way of specifying the code that should be run when it happens. This is the purpose of events and delegates.
Declaring And Using Delegates
What is a delegate?
Advantages
Encapsulating the method's call from caller
Effective use of delegate improves the performance of application
Used to call a method asynchronously
Delegate is a type which holds the method(s) reference in an object. It is also referred to as a type safe function pointer.
Sample Program Using Delegate
public delegate double Delegate_Prod(int a,int b); class Class1 { static double fn_Prodvalues(int val1,int val2) { return val1*val2; } static void Main(string[] args) { //Creating the Delegate Instance Delegate_Prod delObj = new Delegate_Prod(fn_Prodvalues); Console.Write("Please Enter Values"); int v1 = Int32.Parse(Console.ReadLine()); int v2 = Int32.Parse(Console.ReadLine()); //use a delegate for processing double res = delObj(v1,v2); Console.WriteLine ("Result :"+res); Console.ReadLine(); } }
The Automated Factory Scenario
motor control and drive system, robotics system, machine vision system, programmable logic control system and manufacturing executive systems.
Factory automation is based on the application of computers. It involves various technologies. The technologies involves in factory automation are;
These are also known as automation tools without which automation is not possible. There is need to unify the business with plant floor system in the present global scenario. The factory automation makes it possible.
Implementing the Factory Without Using Delegates
A simple approach to implementing the shutdown functionality in the control
program is as follows:
class Controller { // Fields representing the different machines private FoldingMachine folder; private WeldingMachine welder; private PaintingMachine painter; ... public void ShutDown() { folder.StopFolding(); welder.FinishWelding(); painter.PaintOff(); } ... }
Implementing the Factory Without Using Delegates
In most cases, when we call a function, we specify the function to be called directly. If the class MyClass has a function named Process, we'd normally call it like this: using System; namespace Akadia.NoDelegate { public class MyClass { public void Process() { Console.WriteLine("Process() begin"); Console.WriteLine("Process() end"); } } public class Test { static void Main(string[] args) { MyClass myClass = new MyClass(); myClass.Process(); } } }
Implementing the Factory Without Using Delegates
That works well in most situations. Sometimes, however, we don't want to call a function directly - we'd like to be able to pass it to somebody else so that they can call it. This is especially useful in an event-driven system such as a graphical user interface, when I want some code to be executed when the user clicks on a button, or when I want to log some information but can't specify how it is logged.
Implementing the Factory by Using a Delegate
The general format of each method, therefore, is this
void methodName();
We declare a delegate like this;
delegate void stopMachineryDelegate();
We can create an instance and make it refer to a matching method by using the +=
compound assignment operator. We can do this in the constructor of the controller class
like this:
Implementing the Factory by Using a Delegate
class Controller { delegate void stopMachineryDelegate(); private stopMachineryDelegate stopMachinery; // an instance of the delegate ... public Controller() { this.stopMachinery += folder.StopFolding; } ... }
Implementing the Factory by Using a Delegate
It is safe to use the += operator on an uninitialized delegate. It will be initialized
automatically.
Alternatively, we can also use the new keyword to initialize a delegate explicitly with a
single specific method, like this: this.stopMachinery = new stopMachineryDelegate(folder.StopFolding);
We can call the method by invoking the delegate, like this:
public void ShutDown() { this.stopMachinery(); ... }
Implementing the Factory by Using a Delegate
The principal advantage of using a delegate is that it can refer to more than one method;
we simply use the += operator to add methods to the delegate, like this:
public Controller() { this.stopMachinery += folder.StopFolding; this.stopMachinery += welder.FinishWelding; this.stopMachinery += painter.PaintOff; }
We can remove a method from a delegate by using the –= compound assignment
operator:
this.stopMachinery -= folder.StopFolding;
Implementing the Factory by Using a Delegate
The current scheme adds the machine methods to the delegate in the Controller
constructor. To make the Controller class totally independent of the various machines, we
need to make stopMachineryDelegate type public and supply a means of enabling
classes outside Controller to add methods to the delegate.
We have several options:
• Make the delegate variable, stopMachinery, public :
public stopMachineryDelegate stopMachinery;
• Keep the stopMachinery delegate variable private, but provide a read/write property to
provide access to it:
Implementing the Factory by Using a Delegate
public delegate void stopMachineryDelegate();
...
public stopMachineryDelegate StopMachinery
{
get
{
return this.stopMachinery;
}
set
{
this.stopMachinery = value;
}
}
Implementing the Factory by Using a Delegate
• Provide complete encapsulation by implementing separate Add and Remove methods.
The Add method takes a method as a parameter and adds it to the delegate, while
the Remove method removes the specified method from the delegate (notice that we
specify a method as a parameter by using a delegate type):
public void Add(stopMachineryDelegate stopMethod)
{
this.stopMachinery += stopMethod;
}
public void Remove(stopMachineryDelegate stopMethod)
{
this.stopMachinery -= stopMethod;
}
Implementing the Factory by Using a Delegate
Whichever technique we choose, we should remove the code that adds the machine
Methods to the delegate from the Controller constructor. we can then instantiate a
Controller and objects representing the other machines like this (this example uses the
Add/Remove approach):
Controller control = new Controller();
FoldingMachine folder = new FoldingMachine();
WeldingMachine welder = new WeldingMachine();
PaintingMachine painter = new PaintingMachine();
...
control.Add(folder.StopFolding);
control.Add(welder.FinishWelding);
control.Add(painter.PaintOff);
...
control.ShutDown();
...
Using Delegates
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
delegate void MyDelegate(string s);
class MyClass
{
public static void Hello(string s)
{
Console.WriteLine(" Hello, {0}!", s);
}
public static void Goodbye(string s)
{
Console.WriteLine(" Goodbye, {0}!", s);
}
Using Delegates
public static void Main()
{
MyDelegate a, b, c, d;
// Create the delegate object a that references
// the method Hello:
a = new MyDelegate(Hello);
// Create the delegate object b that references
// the method Goodbye:
b = new MyDelegate(Goodbye);
// The two delegates, a and b, are composed to form c:
c = a + b;
// Remove a from the composed delegate, leaving d,
// which calls only the method Goodbye:
d = c - a;
Console.WriteLine("Invoking delegate a:");
a("A");
Console.WriteLine("Invoking delegate b:");
b("B");
Console.WriteLine("Invoking delegate c:");
c("C");
Console.WriteLine("Invoking delegate d:");
d("D");
}
}
Using Delegates
This example demonstrates composing delegates. A useful property of delegate objects is that
they can be composed using the "+" operator. A composed delegate calls the two delegates it was
composed from. Only delegates of the same type can be composed.
The "-" operator can be used to remove a component delegate from a composed delegate.
A very basic example :
using System;
namespace Akadia.BasicDelegate
{
// Declaration
public delegate void SimpleDelegate();
class TestDelegate
{
public static void MyFunc()
{
Console.WriteLine("I was called by delegate ...");
}
Using Delegates
public static void Main()
{
// Instantiation
SimpleDelegate simpleDelegate = new SimpleDelegate(MyFunc);
// Invocation
simpleDelegate();
}
}
}
Lambda Expressions and Delegates
All the examples of adding a method to a delegate that we have seen so far use the
method’s name. For example, returning to the automated factory scenario described
earlier, we add the StopFolding method of the folder object to the stopMachinery delegate
like this:
this.stopMachinery += folder.StopFolding;
This approach is very useful if there is a convenient method that matches the signature of
the delegate, but what if this is not the case? Suppose that the StopFolding method
actually had the following signature:
void StopFolding(int shutDownTime); // Shut down in the specified number of seconds
This signature is now different from that of the FinishWelding and PaintOff methods, and
therefore we cannot use the same delegate to handle all three methods. So, what do
we do?
Creating a Method Adapter
One way around this problem is to create another method that calls StopFolding but that
takes no parameters itself, like this:
void FinishFolding()
{
folder.StopFolding(0); // Shut down immediately
}
we can then add the FinishFolding method to the stopMachinery delegate in place of the
StopFolding method, using the same syntax as before:
this.stopMachinery += folder.FinishFolding;
When the stopMachinery delegate is invoked, it calls FinishFolding, which in turn calls the
StopFolding method, passing in the parameter of 0.
Using a Lambda Expression as an Adapter
A lambda expression contains two of these elements: a list of parameters and a method
body.
Lambda expressions do not define a method name, and the return type (if any) is inferred
from the context in which the lambda expression is used. In the StopFolding method of
the FoldingMachine class, the problem is that this method now takes a parameter, so we
need to create an adapter that takes no parameters that we can add to the
stopMachinery delegate.
We can use the following statement to do this:
this.stopMachinery += (() => { folder.StopFolding(0); });
Using a Lambda Expression as an Adapter
The body of a lambda expression can be a method body containing
multiple statements, or it can actually be a single expression. If the body of a lambda
expression contains only a single expression, we can omit the braces and the semicolon
(but we still need a semicolon to complete the entire statement), like this:
this.stopMachinery += (() => folder.StopFolding(0));
When we invoke the stopMachinery delegate, it will run the code defined by the lambda
expression.
The Form of Lambda Expressions
A lambda expression is an anonymous function that we can use to create delegates or
expression tree types. By using lambda expressions, we can write local functions that
can be passed as arguments or returned as the value of function calls. Lambda
expressions are particularly helpful for writing LINQ query expressions.
To create a lambda expression, we specify input parameters (if any) on the left side of the
lambda operator =>, and we put the expression or statement block on the other side. For
example, the lambda expression x => x * xspecifies a parameter that’s named x and
returns the value of x squared. we can assign this expression to a delegate type, as the
following example shows
The Form of Lambda Expressions
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Akadia.BasicDelegate
{
// Declaration
public delegate void SimpleDelegate();
class TestDelegate
{
delegate int del(int i);
static void Main(string[] args)
{
del myDelegate = x => x * x;
int j = myDelegate(5); //j = 25
}
}
}
The Form of Lambda Expressions
To create an expression tree type:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace ConsoleApplication1
{
class Program
{
delegate int del(int i);
static void Main(string[] args)
{
Expression<del> myET = x => x * x;
}
}
}
Enabling Notifications with Events
we declare an event similarly to how we declare a field. However, because events are
İntended to be used with delegates, the type of an event must be a delegate, and we
must prefix the declaration with the event keyword. Use the following syntax to declare an
event:
event delegateTypeName eventName
As an example, here’s the StopMachineryDelegate delegate from the automated factory.
It has been relocated to a new class called TemperatureMonitor, which provides an
interface to the various electronic probes monitoring the temperature of the equipment
(this is a more logical place for the event than the Controller class is):
class TemperatureMonitor
{
public delegate void StopMachineryDelegate();
...
}
Declaring an Event
Enabling Notifications with Events
We can define the MachineOverheating event, which will invoke the
stopMachineryDelegate, like this:
class TemperatureMonitor
{
public delegate void StopMachineryDelegate();
public event StopMachineryDelegate MachineOverheating;
...
}
Declaring an Event
Enabling Notifications with Events
We can define the MachineOverheating event, which will invoke the Like delegates, events come
ready-made with a += operator. we subscribe to an event by
using this += operator. In the automated factory, the software controlling each machine
can arrange for the shutdown methods to be called when the MachineOverheating event is raised, like
this:
class TemperatureMonitor
{
public delegate void StopMachineryDelegate();
public event StopMachineryDelegate MachineOverheating;
...
}
...
TemperatureMonitor tempMonitor = new TemperatureMonitor();
...
tempMonitor.MachineOverheating += (() => { folder.StopFolding(0); });
tempMonitor.MachineOverheating += welder.FinishWelding;
tempMonitor.MachineOverheating += painter.PaintOff;
Subscribing to an Event
Enabling Notifications with Events
An event can be raised, just like a delegate, by calling it like a method. When we raise
an event, all the attached delegates are called in sequence. For example, here’s the
TemperatureMonitor class with a private Notify method that raises the
MachineOverheating event:
class TemperatureMonitor
{
public delegate void StopMachineryDelegate();
public event StopMachineryDelegate MachineOverheating;
...
private void Notify()
{
if (this.MachineOverheating != null)
{
this.MachineOverheating();
}
}
...
}
Raising an Event
Understanding WPF User Interface Events
The WPF Button class derives from the ButtonBase class, inheriting a public event called
Click of type RoutedEventHandler. The RoutedEventHandler delegate expects two
parameters: a reference to the object that caused the event to be raised and a
RoutedEventArgs object that contains additional information about the event:
public delegate void RoutedEventHandler(Object sender, RoutedEventArgs e);
The Button class looks like this:
public class ButtonBase: ...
{
public event RoutedEventHandler Click;
...
}
public class Button: ButtonBase
{
...
}
Understanding WPF User Interface Events
The Button class automatically raises the Click event when we click the button on-
screen.(How this actually happens is beyond the scope of this book.) This arrangement
makes it easy to create a delegate for a chosen method and attach that delegate to the
required event. The following example shows the code for a WPF form that contains a
button called okay and the code to connect the Click event of the okay button to the
okayClick method:
public partial class Example : System.Windows.Window, System.Windows.Markup.
IComponentConnector
{
internal System.Windows.Controls.Button okay;
...
void System.Windows.Markup.IComponentConnector.Connect(...)
{
...
this.okay.Click += new System.Windows.RoutedEventHandler(this.okayClick);
...
}
...
}
Understanding WPF User Interface Events
This code is usually hidden from we. When we use the Design View window in Visual
Studio 2010 and set the Click property of the okay button to okayClick in the Extensible
Application Markup Language (XAML) description of the form, Visual Studio 2010
generates this code for we. All we have to do is write wer application logic in the event
handling method, okayClick, in the part of the code that we do have access to, in the
Example.xaml.cs file in this case: public partial class Example : System.Windows.Window
{
...
private void okayClick(object sender, RoutedEventArgs args)
{
// wer code to handle the Click event
}
}
Using Events
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace wildert
{
public class Metronome
{
public event TickHandler Tick;
public EventArgs e = null;
public delegate void TickHandler(Metronome m, EventArgs e);
public void Start()
{
while (true)
{
System.Threading.Thread.Sleep(3000);
if (Tick != null)
{
Tick(this, e);
}
}
}
Using Events
public class Listener
{
public void Subscribe(Metronome m)
{
m.Tick += new Metronome.TickHandler(HeardIt);
}
private void HeardIt(Metronome m, EventArgs e)
{
System.Console.WriteLine("HEARD IT");
}
}
class Test
{
static void Main()
{
Metronome m = new Metronome();
Listener l = new Listener();
l.Subscribe(m);
m.Start();
}
}
}
Using Events
A slightly more complicated example is if the event has
information passed with it, such as mouse coordinates for a
mouse event or which key is pressed for a keypress event. To do this
we need to create an appropriate class derived from
the EventArgs class and then set an instance of it before raising
the event. See below:
Using Events
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
namespace wildert
{
public class TimeOfTick : EventArgs
{
private DateTime TimeNow;
public DateTime Time
{
set
{
TimeNow = value;
}
get
{
return this.TimeNow;
}
}
}
Using Events
public class Metronome
{
public event TickHandler Tick;
public delegate void TickHandler(Metronome m, TimeOfTick e);
public void Start()
{
while (true)
{
System.Threading.Thread.Sleep(3000);
if (Tick != null)
{
TimeOfTick TOT = new TimeOfTick();
TOT.Time = DateTime.Now;
Tick(this, TOT);
}
}
}
}
Using Events
public class Listener
{
public void Subscribe(Metronome m)
{
m.Tick += new Metronome.TickHandler(HeardIt);
}
private void HeardIt(Metronome m, TimeOfTick e)
{
System.Console.WriteLine("HEARD IT AT {0}", e.Time);
}
}
class Test
{
static void Main()
{
Metronome m = new Metronome();
Listener l = new Listener();
l.Subscribe(m);
m.Start();
}
}
}