Delegates and Events in C#
Delegates and Events in C#
A Delegate is basically
function pointer. A delegate in C# is similar to a function pointer in C or
C++. Using a delegate allows the programmer to encapsulate a reference to a
method inside a delegate object. The delegate object can then be passed to code
which can call the referenced method, without having to know at compile time
which method will be invoked.
Simple No Delegate Example:
using System;
namespace NoDelegate
{
public class MySimpleClass
{
public void Method1()
{
Console.WriteLine("Method() begin");
Console.WriteLine("Method() end");
}
}
public class MyAnotherClass
{
static void Main(string[] args)
{
MySimpleClass myClass = new MySimpleClass();
myClass.Method1();
}
}
}
namespace NoDelegate
{
public class MySimpleClass
{
public void Method1()
{
Console.WriteLine("Method() begin");
Console.WriteLine("Method() end");
}
}
public class MyAnotherClass
{
static void Main(string[] args)
{
MySimpleClass myClass = new MySimpleClass();
myClass.Method1();
}
}
}
In most cases, when we
call a function, we specify the function to be called directly. As we
called in above snippet myClass.Method1().
Delegate Basics
An interesting and useful
property of a delegate is that it does not know or care about the class of the
object that it references. Any object will do; all that matters is that the
method's argument types and return type match the delegate's. This makes delegates perfectly
suited for "anonymous" invocation.
The signature of a delegate is shown below:
delegate
result-type identifier ([parameters]);
where:
- result-type:
The result type, which matches the return type of the function.
- identifier:
The delegate name.
- parameters:
The Parameters, that the function takes.
The declaration for a delegate looks just like
the declaration for a function, except that in this case, we're declaring the
signature of functions that this delegate can reference.
There are three steps in defining and using
delegates:
- Declaration
- Instantiation
- Invocation
Simple Delegate Example 1:
using System;
namespace MySimpleDelegate
{
// Step 1: Declaration
public delegate void SimpleDelegate();
class MyDelegateClass
{
public static void MyFunc()
{
Console.WriteLine("I was called by delegate ...");
}
public static void Main()
{
// Step 2: Instantiation
SimpleDelegate simpleDelegate = new SimpleDelegate(MyFunc);
// Step 3: Invocation
simpleDelegate();
}
}
}
namespace MySimpleDelegate
{
// Step 1: Declaration
public delegate void SimpleDelegate();
class MyDelegateClass
{
public static void MyFunc()
{
Console.WriteLine("I was called by delegate ...");
}
public static void Main()
{
// Step 2: Instantiation
SimpleDelegate simpleDelegate = new SimpleDelegate(MyFunc);
// Step 3: Invocation
simpleDelegate();
}
}
}
Bit a complex example of delegate
that takes a single parameter and has no return type.
using System;
namespace MySimpleDelegate
{
// Delegate Specification
public class MyDelegateClass
{
// Step 1: Declaration
namespace MySimpleDelegate
{
// Delegate Specification
public class MyDelegateClass
{
// Step 1: Declaration
public delegate void LogHandler(string message);
public void Process(LogHandler logHandler)
{
public void Process(LogHandler logHandler)
{
// Step 3: Invocation (Checking for null)
if (logHandler != null)
{
logHandler("Process() begin");
}
if (logHandler != null)
{
logHandler ("Process() end");
}
}
}
public class MyTestApplication
{
// Static Function: To which is used in the Delegate. To call the Process()
// function, we need to declare a logging function: Logger() that matches
// the signature of the delegate.
static void Logger(string s)
{
Console.WriteLine(s);
}
static void Main(string[] args)
{
MyDelegateClass myClass = new MyDelegateClass();
if (logHandler != null)
{
logHandler("Process() begin");
}
if (logHandler != null)
{
logHandler ("Process() end");
}
}
}
public class MyTestApplication
{
// Static Function: To which is used in the Delegate. To call the Process()
// function, we need to declare a logging function: Logger() that matches
// the signature of the delegate.
static void Logger(string s)
{
Console.WriteLine(s);
}
static void Main(string[] args)
{
MyDelegateClass myClass = new MyDelegateClass();
// Step
2: Instantiation of delegate, pointing to the logging function.
// This delegate will then be passed to the Process() function.
MyClass.LogHandler myLogger = new MyClass.LogHandler(Logger);
myClass.Process(myLogger);
}
}
// This delegate will then be passed to the Process() function.
MyClass.LogHandler myLogger = new MyClass.LogHandler(Logger);
myClass.Process(myLogger);
}
}
}
Simple Delegate Example 2:
using System;
using System.IO;
namespace MySimpleDelegate
{
public class MyDelegateClass
{
// Step 1: Declaration
public delegate void LogHandler(string message);
public void Process(LogHandler logHandler)
{
using System.IO;
namespace MySimpleDelegate
{
public class MyDelegateClass
{
// Step 1: Declaration
public delegate void LogHandler(string message);
public void Process(LogHandler logHandler)
{
// Step
3: Invocation (Checking for null)
if (logHandler != null)
{
logHandler("Process() begin");
}
if (logHandler != null)
{
logHandler ("Process() end");
}
}
}
public class FileLogger
{
FileStream fileStream;
StreamWriter streamWriter;
// Constructor
public FileLogger(string filename)
{
fileStream = new FileStream(filename, FileMode.Create);
streamWriter = new StreamWriter(fileStream);
}
// Member Function which is used in the Delegate
public void Logger(string s)
{
streamWriter.WriteLine(s);
}
public void Close()
{
streamWriter.Close();
fileStream.Close();
}
}
public class MyTestApplication
{
static void Main(string[] args)
{
FileLogger fl = new FileLogger("process.log");
MyDelegateClass myClass = new MyDelegateClass();
// Step 2: instance of the delegate, pointing to the Logger()
// function on the fl instance of a FileLogger.
MyClass.LogHandler myLogger = new MyClass.LogHandler(fl.Logger);
myClass.Process(myLogger);
fl.Close();
}
}
}
if (logHandler != null)
{
logHandler("Process() begin");
}
if (logHandler != null)
{
logHandler ("Process() end");
}
}
}
public class FileLogger
{
FileStream fileStream;
StreamWriter streamWriter;
// Constructor
public FileLogger(string filename)
{
fileStream = new FileStream(filename, FileMode.Create);
streamWriter = new StreamWriter(fileStream);
}
// Member Function which is used in the Delegate
public void Logger(string s)
{
streamWriter.WriteLine(s);
}
public void Close()
{
streamWriter.Close();
fileStream.Close();
}
}
public class MyTestApplication
{
static void Main(string[] args)
{
FileLogger fl = new FileLogger("process.log");
MyDelegateClass myClass = new MyDelegateClass();
// Step 2: instance of the delegate, pointing to the Logger()
// function on the fl instance of a FileLogger.
MyClass.LogHandler myLogger = new MyClass.LogHandler(fl.Logger);
myClass.Process(myLogger);
fl.Close();
}
}
}
The cool part in above 2
simple examples is that we didn't have to change the Process() function; the
code to all the delegate is the same regardless of whether it refers to a static or member function.
In C#, delegates are multicast,
which means that they can point to more than one function at a time (that
is, they're based off the System.MulticastDelegate type). A multicast delegate
maintains a list of functions that will all be called when the delegate is
invoked. We can add back in the logging function from the first 2 simple examples,
and call both delegates. Here's what the code looks like:
using System;
using System.IO;
namespace MySimpleDelegate
{
// Delegate Specification
public class MyDelegateClass
{
// Step 1: Declaration
public delegate void LogHandler(string message);
public void Process(LogHandler logHandler)
{
if (logHandler != null)
{
logHandler("Process() begin");
}
if (logHandler != null)
{
logHandler ("Process() end");
}
}
}
public class FileLogger
{
FileStream fileStream;
StreamWriter streamWriter;
public FileLogger(string filename)
{
fileStream = new FileStream(filename, FileMode.Create);
streamWriter = new StreamWriter(fileStream);
}
// Member Function used in the Delegate
public void Logger(string s)
{
streamWriter.WriteLine(s);
}
public void Close()
{
streamWriter.Close();
fileStream.Close();
}
}
public class TestApplication
{
// Static Function used in the Delegate
static void Logger(string s)
{
Console.WriteLine(s);
}
static void Main(string[] args)
{
FileLogger fl = new FileLogger("process.log");
MyClass myClass = new MyClass();
// Step 2: instance of the delegate
MyClass.LogHandler myLogger = null;
myLogger += new MyClass.LogHandler(Logger);
myLogger += new MyClass.LogHandler(fl.Logger);
myClass.Process(myLogger);
fl.Close();
}
}
}
using System.IO;
namespace MySimpleDelegate
{
// Delegate Specification
public class MyDelegateClass
{
// Step 1: Declaration
public delegate void LogHandler(string message);
public void Process(LogHandler logHandler)
{
if (logHandler != null)
{
logHandler("Process() begin");
}
if (logHandler != null)
{
logHandler ("Process() end");
}
}
}
public class FileLogger
{
FileStream fileStream;
StreamWriter streamWriter;
public FileLogger(string filename)
{
fileStream = new FileStream(filename, FileMode.Create);
streamWriter = new StreamWriter(fileStream);
}
// Member Function used in the Delegate
public void Logger(string s)
{
streamWriter.WriteLine(s);
}
public void Close()
{
streamWriter.Close();
fileStream.Close();
}
}
public class TestApplication
{
// Static Function used in the Delegate
static void Logger(string s)
{
Console.WriteLine(s);
}
static void Main(string[] args)
{
FileLogger fl = new FileLogger("process.log");
MyClass myClass = new MyClass();
// Step 2: instance of the delegate
MyClass.LogHandler myLogger = null;
myLogger += new MyClass.LogHandler(Logger);
myLogger += new MyClass.LogHandler(fl.Logger);
myClass.Process(myLogger);
fl.Close();
}
}
}
Events
An event is a member that
enables a class or object to provide notifications. An event is declared like a
field except that the declaration includes an event keyword and the type must
be a delegate type.
The following important conventions are used
with events:
- Event
Handlers in the .NET Framework return void and take two parameters.
- The
first paramter is the source of the event; that is the publishing object.
- The
second parameter is an object derived from EventArgs.
- Events
are properties of the class publishing the event.
- The
keyword event controls how the event property is accessed by the
subscribing classes.
Let's modify our logging
example from above to use an event rather than a delegate:
using System;
using System.IO;
namespace MySimpleEvent
{
/* ========= Event Class ============== */
public class MyEventClass
{
// Step 1: Declaration
public delegate void LogHandler(string message);
// Define an Event based on the above Delegate
public event LogHandler Log;
// Instead of having the Process() function take a delegate
// as a parameter, we've declared a Log event. Call the Event,
// using the OnXYZ Method, where XYZ is the name of the Event.
public void Process()
{
OnLog("Process() begin");
OnLog("Process() end");
}
// By Default, create an OnXYZ Method, to call the Event
protected void OnLog(string message)
{
if (Log != null)
{
Log(message);
}
}
}
// The FileLogger class merely encapsulates the file I/O
public class FileLogger
{
FileStream fileStream;
StreamWriter streamWriter;
// Constructor
public FileLogger(string filename)
{
fileStream = new FileStream(filename, FileMode.Create);
streamWriter = new StreamWriter(fileStream);
}
// Member Function which is used in the Delegate
public void Logger(string s)
{
streamWriter.WriteLine(s);
}
public void Close()
{
streamWriter.Close();
fileStream.Close();
}
}
/* ========= Observer of the Event ============== */
// It's now easier and cleaner to merely add instances
// of the delegate to the event, instead of having to
// manage things ourselves
public class MyTestApplication
{
static void Logger(string s)
{
Console.WriteLine(s);
}
static void Main(string[] args)
{
FileLogger fl = new FileLogger("process.log");
MyEventClass myClass = new MyEventClass ();
// Subscribe the Functions Logger and fl.Logger
myClass.Log += new MyClass.LogHandler(Logger);
myClass.Log += new MyClass.LogHandler(fl.Logger);
// The Event will now be triggered in the Process() Method
myClass.Process();
fl.Close();
}
}
}
using System.IO;
namespace MySimpleEvent
{
/* ========= Event Class ============== */
public class MyEventClass
{
// Step 1: Declaration
public delegate void LogHandler(string message);
// Define an Event based on the above Delegate
public event LogHandler Log;
// Instead of having the Process() function take a delegate
// as a parameter, we've declared a Log event. Call the Event,
// using the OnXYZ Method, where XYZ is the name of the Event.
public void Process()
{
OnLog("Process() begin");
OnLog("Process() end");
}
// By Default, create an OnXYZ Method, to call the Event
protected void OnLog(string message)
{
if (Log != null)
{
Log(message);
}
}
}
// The FileLogger class merely encapsulates the file I/O
public class FileLogger
{
FileStream fileStream;
StreamWriter streamWriter;
// Constructor
public FileLogger(string filename)
{
fileStream = new FileStream(filename, FileMode.Create);
streamWriter = new StreamWriter(fileStream);
}
// Member Function which is used in the Delegate
public void Logger(string s)
{
streamWriter.WriteLine(s);
}
public void Close()
{
streamWriter.Close();
fileStream.Close();
}
}
/* ========= Observer of the Event ============== */
// It's now easier and cleaner to merely add instances
// of the delegate to the event, instead of having to
// manage things ourselves
public class MyTestApplication
{
static void Logger(string s)
{
Console.WriteLine(s);
}
static void Main(string[] args)
{
FileLogger fl = new FileLogger("process.log");
MyEventClass myClass = new MyEventClass ();
// Subscribe the Functions Logger and fl.Logger
myClass.Log += new MyClass.LogHandler(Logger);
myClass.Log += new MyClass.LogHandler(fl.Logger);
// The Event will now be triggered in the Process() Method
myClass.Process();
fl.Close();
}
}
}
Another example
of Event
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace SecondChangeEvent
{
// Our subject -- it is this class that other classes
// will observe. This class publishes one event:
// SecondChange. The observers subscribe to that event.
public class Clock
{
private int _hour;
private int _min;
private int _sec;
// The delegate named SecondChangeHandler, which will
encapsulate
// any method that takes a clock object and a
TimeInfoEventArgs
// object as the parameter and returns no value. It's the
// delegate the subscribers must implement.
public delegate void SecondChangeHandler(object clock, TimeInfoEventArgs
timeInfo);
public event SecondChangeHandler SecondChange;
protected void
onSecondChange(object clock, TimeInfoEventArgs timeInfo)
{
if (SecondChange != null)
{
SecondChange(clock, timeInfo);
}
}
public void Run()
{
for
(; ; )
{
Thread.Sleep(1000);
DateTime dt = DateTime.Now;
if (dt.Second != _sec)
{
TimeInfoEventArgs timeInfo = new TimeInfoEventArgs(dt.Hour,
dt.Minute, dt.Second);
onSecondChange(this, timeInfo);
}
_hour= dt.Hour;
_min = dt.Minute;
_sec = dt.Second;
}
}
}
// The class to hold the information about the event
// in this case it will hold only information
// available in the clock class, but could hold
// additional state information
public class TimeInfoEventArgs : EventArgs
{
public readonly int hour;
public readonly int min;
public readonly int sec;
public TimeInfoEventArgs(int
hour, int min, int
sec)
{
this.hour = hour;
this.min = min;
this.sec = sec;
}
}
// An observer. DisplayClock subscribes to the
// clock's events. The job of DisplayClock is
// to display the current time
public class DisplayClock
{
// Given a clock, subscribe to
// its SecondChangeHandler event
public void
Subscribe(Clock theClock)
{
theClock.SecondChange += new Clock.SecondChangeHandler(TimeHasChanged);
}
// The method that implements the delegated functionality
public void
TimeHasChanged(object theClock, TimeInfoEventArgs ti)
{
Console.WriteLine("Current
Time: {0}:{1}:{2}",
ti.hour.ToString(),
ti.min.ToString(),
ti.sec.ToString());
}
}
}
TestApplication code:
SecondChangeEvent.Clock
myClock = new SecondChangeEvent.Clock();
// Create the
display and tell it to
// Observe the clock just created
// Observe the clock just created
SecondChangeEvent.DisplayClock
dc = new SecondChangeEvent.DisplayClock();
dc.Subscribe(myClock);
// Get the clock
started
myClock.Run();
The observer
classes do not need to know how the Clock works, and the Clock does not need to
know what they are going to do in response to the event. The advantage of the events
is that any number of classes can be notified when an event is raised.
Lighting, DJ and Projector is the main focus of the attraction for any type of events, as well as birthday, anniversary, marriage, etc. Contact us for complete event management services at affordable cost. av rental dallas | audio rental dallas | event lighting rental dallas | dj services provider in dallas | projector screen rental
ReplyDelete