You are on page 1of 54

CHAP TE R 19

Enumerating collections
After completing this chapter, you will be
able to:

Manually define an enumerator that you can use to


iterate over the elements in a collection.

Implement an enumerator automatically by creating an


iterator.

Provide additional iterators that can step through the


elements of a collection in different sequences.
Enumerating the elements in a collection

using the foreach statement to list the items in a simple


array. The code looks like this:
int[] pins = { 9, 3, 7, 2 };
foreach (int pin in pins)
{
Console.WriteLine(pin);
}
The foreach construct provides an elegant mechanism
that greatly simplifies the code you need to write, but
it can be exercised only under certain circumstances
Note
Remember that all arrays in C# are actually
instances of the System.Array class.
 The System.Array class is a collection class
that implements the IEnumerable interface.
The IEnumerable interface contains a single method
called GetEnumerator:
IEnumerator GetEnumerator();
The GetEnumerator method should return an
enumerator object that implements the System.
Collections.IEnumerator interface
Iterators in C#
An iterator is a method in C# which is used in an array or in
collections like the list, etc. to retrieve elements one by one.
 Or in other words, we can say that an iterator is used to
perform an iteration over the collections.
This feature is introduced in C# 2.0.
 It uses the yield return statement to return the element
from the collection at a time and it always remembers the
current location of the iterator, so when the next iteration
takes place it will return the next element of the given
collection.
 If you want to stop the iteration you will use the yield
break statement.
The return type of this method is IEnumerable,
IEnumerator.

 Which means by using iterator compiler will


automatically create IEnumerable or IEnumerator
interface for you there is no need to implement
IEnumerable or IEnumerator interface in your class for
using a foreach loop.

When the compiler identifies an iterator in your class it


will automatically create the current, MoveNext and
dispose of the method of IEnumerable or IEnumerator
interface.
the first iteration of the foreach loop causes execution
to proceed in the SomeNumbers iterator method until
the first yield return statement is reached.
This iteration returns a value of 3, and the current
location in the iterator method is retained.
 On the next iteration of the loop, execution in the
iterator method continues from where it left off, again
stopping when it reaches a yield return statement.
This iteration returns a value of 5, and the current
location in the iterator method is again retained.
The loop completes when the end of the iterator
method is reached.
Note
Remember that all arrays in C# are actually
instances of the System.Array class.
 The System.Array class is a collection class
that implements the IEnumerable interface.
The IEnumerable interface contains a single method
called GetEnumerator:
IEnumerator GetEnumerator();
The GetEnumerator method should return an
enumerator object that implements the System.
Collections.IEnumerator interface
The enumerator object is used for stepping through (enumerating) the
elements of the collection.
 The IEnumerator interface specifies the following property and methods:

1. object Current { get; }


2. bool MoveNext();
3. void Reset();
Think of an enumerator as a pointer indicating elements in a
list. Initially, the pointer points before the first element.
 You call the MoveNext method to move the pointer down to
the next (first) item in the list; the MoveNext method should
return true if there actually is another item and false if there
isn’t.
You use the Current property to access the item currently
pointed to, and you use the Reset method to return the pointer
back to before the first item in the list.
By creating an enumerator by using the
GetEnumerator method of a collection and repeatedly
calling the MoveNext method and retrieving the value
of the Current property by using the enumerator, you
can move forward through the elements of a collection
one item at a time.

This is exactly what the foreach statement does.


Manually implementing an enumerator

In the next exercise, you will define a class that implements
the generic IEnumerator<T> interface and create an
enumerator for the binary tree class that is demonstrated in
Chapter 17,
1. Create the TreeEnumerator class
2. In the Code and Text Editor window displaying the
TreeEnumerator.cs file, modify the definition of the
TreeEnumerator class to satisfy these requirements
3. Add the three private variables shown in the following
code in bold to the TreeEnumerator <TItem> class:

The currentData variable will be used to hold a


reference to the tree being enumerated, and the
currentItem variable will hold the value returned by
the Current property.
You will populate the enumData queue with the values
extracted from the nodes in the tree, and the MoveNext
method will return each item from this queue in turn.
4. Add a constructor that takes a single Tree<TItem>
parameter called data to the TreeEnumerator<TItem>
class. In the body of the constructor, add a statement that
initializes the currentData variable to data:
Add the following private method called populate to
the TreeEnumerator<TItem> class, immediately after
the constructor:

This method walks the binary tree, adding the data it contains to the queue. The main
difference is that rather than the method appending NodeData values to a string, it stores
these values in the queue.
Update the body of the MoveNext method with the
code shown in bold here:
the MoveNext method should just move through data
items until there are no more left, dequeuing items
until the queue is empty, as in this example.
 It is important to keep in mind that MoveNext does not
actually return data items—that is the purpose of the
Current property.
All MoveNext does is update the internal state in the
enumerator (that is, the value of the currentItem
variable is set to the data item extracted from the
queue) for use by the Current property, returning true
if there is a next value and false otherwise.
 The Current property examines the enumData variable to ensure that
MoveNext has been called. (This variable will be null prior to the first
call to MoveNext.)
 If this is not the case, the property throws an InvalidOperationException
—this is the conventional mechanism used by .NET Framework
applications to indicate that an operation cannot be performed in the
current state.
 If MoveNext has been called beforehand, it will have updated the
currentItem variable, so all the Current property needs to do is return the
value in this variable.
Implementing an enumerator by using an iterator
An iterator is a block of code that yields an ordered
sequence of values.

An iterator is not actually a member of an enumerable


class; rather, it specifies the sequence that an
enumerator should use for returning its values.

In other words, an iterator is just a description of the


enumeration sequence that the C# compiler can use for
creating its own enumerator.
A simple iterator

The following BasicCollection<T> class illustrates the


principles of implementing an iterator.

 The class uses a List<T> object for holding data and


provides the FillList method for populating this list.

 Notice also that the BasicCollection<T> class implements


the IEnumerable<T> interface.

The GetEnumerator method is implemented by using an


iterator:
The key point is the use of the yield keyword.
The yield keyword indicates the value that should be
returned by each iteration.
 If it helps, you can think of the yield statement as
calling a temporary halt to the method, passing back a
value to the caller.
When the caller needs the next value, the
GetEnumerator method continues at the point at which
it left off, looping around and then yielding the next
value.
Eventually, the data is exhausted, the loop finishes,
and the GetEnumerator method terminates. At this
point, the iteration is complete.
CHAP TE R 2 0
Decoupling application logic
and handling events
A ft er co mp letin g th is cha p ter, y o u w ill be a ble t o:

Declare a delegate type to create an abstraction of a


method signature.
Create an instance of a delegate to refer to a specific
method.
Call a method through a delegate.
Define a lambda expression to specify the code to be
executed by a delegate.
Declare an event field.
Handle an event by using a delegate.
Raise an event.
Understanding delegates
A delegate is a reference to a method. It is a very simple
concept with extraordinarily powerful implications.
Note Delegates are so named because they “delegate”
processing to the referenced method when they are invoked.
Typically, when you write a statement that invokes a
method, you specify the name of the method (and possibly
specify the object or structure to which the method belongs).
 It is clear from your code exactly which method you are
running and when you are running it. Look at the following
simple example that calls the performCalculation method of
a Processor object
 Processor p = new Processor();
 p.performCalculation();
What is Delegate?
Delegates are a class that holds function references for .Net (more
specifically, the Common Language Infrastructure (CLI)).
 If you compare them with C++ function pointers, C++ function
pointers do nothing more than hold the memory location of a
function.
 And the main difference between C++ function pointers and C#
delegates is, a function pointer is not type safe whereas C#
delegates are a type safe class that defines a return type and a
type of parameter that can be passed for methods.
 C# delegates are the same as passing methods to another method.
 A C# delegate is type safe because whenever you need to declare
any delegate in your program you need to specify the return type
and parameter numbers, type and sequence of methods that's
going to be passed for delegates.
 How to declare a delegate?
For declaring a delegate in a C# program you can use the
following syntax:
<Access Modifier> <delegate> <Return Type> <Delegate
Name>(<Parameter's>)
Where:
<Access Modifier> can be public, private, protected and so on.
<Delegate> is a pre-defined keyword in C#.
<Return Type> can be any type depending on your referenced
function.
<Delegate Name> can be any name.
<Parameter> will also depend on the referenced function.
public delegate int delegate_test(int G, int F, int G);

 A delegate will call only a method which agrees with


its signature and return type.
A method can be a static method associated with a
class or can be an instance method associated with an
object, it doesn’t matter.
Instantiation & Invocation of Delegates
After declaring a delegate, a delegate object is created with
the help of new keyword.
Once a delegate is instantiated, a method call made to the
delegate is pass by the delegate to that method.
The parameters passed to the delegate by the caller are
passed to the method, and the return value, if any, from the
method, is returned to the caller by the delegate.
This is known as invoking the delegate.
[delegate_name] [instance_name] = new
[delegate_name](calling_method_name);
Test GFG = new Test (addnum);
// here,
 // “Test" is delegate name.
 // "GFG" is instance_name
// " addnum " is the calling method.
Let's see a simple example of delegate in C#
which calls add() and mul() methods.
Combine Delegates (Multicast Delegates)

A useful property of delegate objects is that multiple objects


can be assigned to one delegate instance by using
the + operator.

The multicast delegate contains a list of the assigned


delegates.

When the multicast delegate is called, it invokes the


delegates in the list, in order.

Only delegates of the same type can be combined.


The - operator can be used to remove a component delegate
from a multicast delegate.
Error

prog.cs(51,11): error CS1501: No overload for method


`Invoke' takes `1' arguments (1,1): (Location of the
symbol related to previous error)
Enabling notifications by using events

Events are user actions such as key press, clicks,


mouse movements, etc., or some occurrence such as
system generated notifications.

Applications need to respond to events when they


occur.

For example, interrupts. Events are used for inter-
process communication.
Declaring an event
You declare an event in a class intended to act as an event
source.
 An event source is usually a class that monitors its
environment and raises an event when something significant
happens.
In the automated factory, an event source could be a class
that monitors the temperature of each machine.
The temperature-monitoring class would raise a “machine
overheating” event if it detects that a machine has exceeded
its thermal radiation boundary (that is, it has become too
hot).
An event maintains a list of methods to call when it is raised.
publisher-subscriber model
The events are declared and raised in a class and
associated with the event handlers using delegates
within the same class or some other class.
The class containing the event is used to publish the
event. This is called the publisher class.
 Some other class that accepts this event is called
the subscriber class.
 Events use the publisher-subscriber model.
A publisher is an object that contains the definition of
the event and the delegate.
The event-delegate association is also defined in this
object.
A publisher class object invokes the event and it is
notified to other objects.
A subscriber is an object that accepts the event and
provides an event handler.
The delegate in the publisher class invokes the method
(event handler) of the subscriber class.
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
Declaring Events
event delegateTypeName
eventName

 To declare an event inside a class, first a delegate type for


the event must be declared. For example,
public delegate string MyDel(string str);
Next, the event itself is declared, using
the event keyword −
event MyDel MyEvent
Subscribing to an event
Like delegates, events come ready-made with a += operator.
You subscribe to an event by using this += operator and -= for
unsubscribing an events.
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 += welder.FinishWelding;

tempMonitor.MachineOverheating += painter.PaintOff;
Raising an event
You can raise an event, just like a delegate, by calling it
like a method.
When you 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:
Understanding user interface events
C# Button Control
Windows Forms controls are reusable components that
encapsulate user interface functionality and are used in
client side Windows applications.
A button is a control, which is an interactive
component that enables users to communicate with an
application.
The Button class inherits directly from the ButtonBase
class.
A Button can be clicked by using the mouse, ENTER
key, or SPACEBAR if the button has focus.

You might also like