24
Unit 4: Interfaces, Collections, Delegates & Events 1 Prof. Sushant S.Sundikar C# Programming Unit 4: Interfaces, Collections, Delegates & Events . Defining Interfaces in C# An interface is nothing more than a named collection of semantically related abstract members. The specific members defined by an interface depend on the exact behavior it is modeling.. An interface expresses a behavior that a given class or structure may choose to support. Interfaces are declared by using the interface keyword. Here is a simplified form of an interface declaration: interface name { ret-type method-name1(param-list); ret-type method-name2(param-list); // ... ret-type method-nameN(param-list); } The name of the interface is specified by name. Methods are declared using only their return type and signature. They are, essentially, abstract methods. As explained, in an interface, no method can have an implementation. Thus, each class that includes an interface must implement all of the methods. In an interface, methods are implicitly public, and no explicit access specifier is allowed. Here is an example of an interface. It specifies the interface to a class that generates a series of numbers. public interface ISeries { int GetNext(); // return next number in series void Reset(); // restart void SetStart(int x); // set starting value } Program ch04pg01.cs: Program to demonstrate the use of interfaces Implementing an Interface in C# Once an interface has been defined, one or more classes can implement that interface. To implement an interface, the name of the interface is specified after the class name in just the same way that a base class is specified. The general form of a class that implements an interface is shown here: class class-name : interface-name {

Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

  • Upload
    others

  • View
    4

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 1

Prof. Sushant S.Sundikar C# Programming

Unit 4: Interfaces, Collections, Delegates & Events .

Defining Interfaces in C#

An interface is nothing more than a named collection of semantically related abstract members. The

specific members defined by an interface depend on the exact behavior it is modeling.. An interface

expresses a behavior that a given class or structure may choose to support. Interfaces are declared

by using the interface keyword. Here is a simplified form of an interface declaration:

interface name {

ret-type method-name1(param-list);

ret-type method-name2(param-list);

// ...

ret-type method-nameN(param-list);

}

The name of the interface is specified by name. Methods are declared using only their return type

and signature. They are, essentially, abstract methods. As explained, in an interface, no method can

have an implementation. Thus, each class that includes an interface must implement all of the

methods. In an interface, methods are implicitly public, and no explicit access specifier is allowed.

Here is an example of an interface. It specifies the interface to a class that generates a series of

numbers.

public interface ISeries {

int GetNext(); // return next number in series

void Reset(); // restart

void SetStart(int x); // set starting value

}

Program ch04pg01.cs: Program to demonstrate the use of interfaces

Implementing an Interface in C#

Once an interface has been defined, one or more classes can implement that interface. To

implement an interface, the name of the interface is specified after the class name in just the same

way that a base class is specified. The general form of a class that implements an interface is shown

here:

class class-name : interface-name {

Page 2: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 2

Prof. Sushant S.Sundikar C# Programming

// class-body

}

The name of the interface being implemented is specified in interface-name. When a class

implements an interface, the class must implement the entire interface. It cannot pick and choose

which parts to implement.

A class can implement more than one interface. When a class implements more than one interface,

specify each interface in a comma-separated list. A class can inherit a base class and also implement

one or more interfaces. In this case, the name of the base class must come first in the comma-

separated list.

Here is an example that implements the ISeries interface shown earlier.

// Implement ISeries.

class ByTwos : ISeries

{… }

Example 2: class AnotherClass derives from a custom base class MyBaseClass and implements a

single interface ISomeInterface.

public class AnotherClass : MyBaseClass, ISomeInterface

{...}

Contrasting Interfaces to Abstract Base Classes C# allows you to build abstract class types containing abstract methods. However, abstract base

classes do far more than define a group of abstract methods. They are free to define public, private,

and protected state data, as well as any number of concrete methods that can be accessed by the

subclasses.

Interfaces, on the other hand, are pure protocol. Interfaces never define state data and never

provide an implementation of the methods (if you try, you receive a compile-time error):

public interface IAmABadInterface

{

// Error, interfaces can't define data!

int myInt = 0;

// Error, only abstract members allowed!

void MyMethod()

{ Console.WriteLine("Eek!"); }

}

Page 3: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 3

Prof. Sushant S.Sundikar C# Programming

Interface types are also quite helpful given that C# (and .NET-aware languages in general) only

support single inheritance; the interface-based protocol allows a given type to support numerous

behaviors, while avoiding the issues that arise when deriving from extending multiple base classes.

Most importantly, interface-based programming provides yet another way to inject polymorphic

behavior into a system. If multiple classes (or structures) implement the same interface in their

unique ways, you have the power to treat each type in the same manner.

Invoking Interface Members at the Object Level

Assume that we have an interface called IPointy

// Interface IPointy has as a read-only property.

public interface IPointy

{ byte Points{get;} }

And a set of types that support IPointy Interface,

// Hexagon now implements IPointy.

public class Hexagon : Shape, IPointy

{

public Hexagon(){ }

public Hexagon(string name) : base(name){ }

public override void Draw()

{ Console.WriteLine("Drawing {0} the Hexagon", PetName); }

// IPointy Implementation.

public byte Points

{

get { return 6; }

}

}

The next question is how you interact with the new functionality. The most straightforward way to

interact with functionality supplied by a given interface is to invoke the methods directly from the

object level. For example:

static void Main(string[] args)

{

// Call new Points member defined by IPointy.

Hexagon hex = new Hexagon();

Console.WriteLine("Points: {0}", hex.Points);

Console.ReadLine();

}

Page 4: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 4

Prof. Sushant S.Sundikar C# Programming

This approach works fine, however, you will not be able to determine at compile time which

interfaces are supported by a given type. For example, assume you have an array containing 50

Shape-compatible types, only some of which support IPointy. Obviously, if you attempt to invoke the

Points property on a type that has not implemented IPointy, you receive a compile-time error.

To dynamically determine the set of interfaces supported by a type do the following:

• The first way you can determine at runtime if a type supports a specific interface is to make

use of an explicit cast. If the type does not support the requested interface, you receive an

InvalidCastException. To handle this possibility gracefully, make use of structured exception

handling, for example:

// Catch a possible InvalidCastException.

Circle c = new Circle("Lisa");

IPointy itfPt;

try

{

itfPt = (IPointy)c;

Console.WriteLine(itfPt.Points);

}

catch (InvalidCastException e)

{ Console.WriteLine(e.Message); }

• The second way you can determine whether a given type supports an interface is to make

use of the as keyword. If the object can be treated as the specified interface, you are

returned a reference to the interface in question. If not, you receive a null reference:

// Can we treat hex2 as IPointy?

Hexagon hex2 = new Hexagon("Peter");

IPointy itfPt2 = hex2 as IPointy;

if(itfPt2 != null)

Console.WriteLine("Points: {0}", itfPt2.Points);

else

Console.WriteLine("OOPS! Not pointy...");

• You may also check for an implemented interface using the is keyword. If the object in

question is not compatible with the specified interface, you are returned the value false. On

the other hand, if the type is compatible with the interface in question, you can safely call

the members without needing to make use of try/catch logic.

Shape[] s = { new Hexagon(), new Circle(), new

Triangle("Joe"), new Circle("JoJo")} ;

for(int i = 0; i < s.Length; i++)

Page 5: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 5

Prof. Sushant S.Sundikar C# Programming

{

// Recall the Shape base class defines an abstract Draw()

// member, so all shapes know how to draw themselves.

s[i].Draw();

// Who's pointy?

if(s[i] is IPointy)

Console.WriteLine("-> Points: {0} ", ((IPointy)s[i]).Points);

else

Console.WriteLine("-> {0}\'s not pointy!", s[i].PetName);

}

Interfaces as Parameters

Given that interfaces are valid .NET types, you may construct methods that take interfaces as

parameters. To illustrate, assume you have defined another interface named IDraw3D:

// Models the ability to render a type in stunning 3D.

public interface IDraw3D

{

void Draw3D();

}

Next, assume that one of your three shapes (Circle )have been configured to support this new

behavior:

// Circle supports IDraw3D.

public class Circle : Shape, IDraw3D

{

...

public void Draw3D()

{ Console.WriteLine("Drawing Circle in 3D!"); }

}

If you now define a method taking an IDraw3D interface as a parameter, you are able to effectively

send in any object implementing IDraw3D. Consider the following:

// Make some shapes. If they can be rendered in 3D, do it!

public class Program

Page 6: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 6

Prof. Sushant S.Sundikar C# Programming

{

// I'll draw anyone supporting IDraw3D.

public static void DrawIn3D(IDraw3D itf3d)

{

Console.WriteLine("-> Drawing IDraw3D compatible type");

itf3d.Draw3D();

}

static void Main()

{

Shape[] s = { new Hexagon(), new Circle(),

new Triangle(), new Circle("JoJo")} ;

for(int i = 0; i < s.Length; i++)

{

...

// Can I draw you in 3D?

if(s[i] is IDraw3D)

DrawIn3D((IDraw3D)s[i]);

}

}

}

Interfaces as Return Values

Interfaces can also be used as method return values. For example, you could write a method that

takes any System.Object, checks for IPointy compatibility, and returns a reference to the extracted

interface:

// This method tests for IPointy-compatibility and,

// if able, returns an interface reference.

static IPointy ExtractPointyness(object o)

{

if (o is IPointy)

Page 7: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 7

Prof. Sushant S.Sundikar C# Programming

return (IPointy)o;

else

return null;

}

We could interact with this method as follows:

static void Main(string[] args)

{

// Attempt to get IPointy from Car object.

Car myCar = new Car();

IPointy itfPt = ExtractPointyness(myCar);

if(itfPt != null)

Console.WriteLine("Object has {0} points.", itfPt.Points);

else

Console.WriteLine("This object does not implement IPointy");

}

Arrays of Interface Types

Assume you have an array of Interface-compatible objects and that these members all support the

same interface, you can create an array of Interface

// This array can only contain types that implement the IPointy

interface.

IPointy[] myPointyObjects = {new Hexagon(),new Triangle()};

Building Interface Hierarchies

To continue our investigation of creating custom interfaces, let’s examine the topic of interface

hierarchies. Just as a class can serve as a base class to other classes (which can in turn function as

base classes to yet another class), it is possible to build inheritance relationships among interfaces.

As you might expect, the topmost interface defines a general behavior, while the most derived

interface defines more specific behaviors. To illustrate, ponder the following interface hierarchy:

// The base interface.

public interface IDrawable

{ void Draw();}

Page 8: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 8

Prof. Sushant S.Sundikar C# Programming

public interface IPrintable : IDrawable

{ void Print(); }

public interface IMetaFileRender : IPrintable

{ void Render(); }

Figure 4-1 illustrates the chain of inheritance.

Now, if a class wished to support each behavior expressed in this interface hierarchy, it would derive

from the nth-most interface (IMetaFileRender in this case). Any methods defined by the base

interface(s) are automatically carried into the definition. For example:

// This class supports IDrawable, IPrintable, and IMetaFileRender.

public class SuperImage : IMetaFileRender

{

public void Draw()

{ Console.WriteLine("Basic drawing logic."); }

public void Print()

{ Console.WriteLine("Draw to printer."); }

public void Render()

Page 9: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 9

Prof. Sushant S.Sundikar C# Programming

{ Console.WriteLine("Render to metafile."); }

}

Interfaces with Multiple Base Interfaces

As you build interface hierarchies, be aware that it is completely permissible to create an interface

that derives from multiple base interfaces. Recall, however, that it is not permissible to build a class

that derives from multiple base classes. To illustrate, assume you are building a new set of interfaces

that model the automobile behaviors for a particular English agent:

public interface ICar

{ void Drive(); }

public interface IUnderwaterCar

{ void Dive(); }

// Here we have an interface with TWO base interfaces.

public interface IJamesBondCar : ICar, IUnderwaterCar

{ void TurboBoost(); }

Figure 7-6 illustrates the chain of inheritance.

Building Enumerable Types (IEnumerable and IEnumerator)

IEnumerable is the base interface for all non-generic collections that can be enumerated. Assume

you have developed a class named Garage that contains a set of individual Car types stored within a

System.Array

// Garage contains a set of Car objects.

public class Garage

{

private Car[] carArray;

// Fill with some Car objects upon startup.

public Garage()

{

carArray = new Car[4];

carArray[0] = new Car("Rusty", 30);

carArray[1] = new Car("Clunker", 55);

carArray[2] = new Car("Zippy", 30);

Page 10: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 10

Prof. Sushant S.Sundikar C# Programming

carArray[3] = new Car("Fred", 30);

}

}

Ideally, if we iterate over the Garage object’s subitems using the C# foreach construct the compiler

informs you that the Garage class does not implement a method named GetEnumerator().

// This seems reasonable...

public class Program

{

static void Main(string[] args)

{

Garage carLot = new Garage();

// Hand over each car in the collection?

foreach (Car c in carLot)

{

Console.WriteLine("{0} is going {1} MPH",

c.PetName, c.CurrSpeed);

}

}

}

The GetEnumerator() method is implemented by the IEnumerable interface, which is found within

the System.Collections namespace. Objects that support IEnumerable advertise that they are able to

expose items contained to the caller function:

// This interface informs the caller

// that the object's subitems can be enumerated.

public interface IEnumerable

{

IEnumerator GetEnumerator();

}

As seen from above declaration, the GetEnumerator() method returns a reference to yet another

interface named System.Collections.IEnumerator. This interface provides the infrastructure to allow

the caller to traverse the internal objects contained by the IEnumerable-compatible container:

// This interface allows the caller to

// obtain a container's subitems.

public interface IEnumerator

{

bool MoveNext (); // Advance the internal position of the cursor.

object Current { get;} // Get the current item (read-only property).

void Reset (); // Reset the cursor before the first member.

}

If you wish to update the Garage type to support these interfaces, you could take the long road and

implement each method manually.

There is a simpler way to implement customized versions of GetEnumerator(), MoveNext(), Current,

and Reset(). The System.Array type already implements IEnumerable and IEnumerator. You can

simply delegate the request to the System.Array by implementing IEnumerable Interface with

Garage class:

Page 11: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 11

Prof. Sushant S.Sundikar C# Programming

using System.Collections;

public class Garage : IEnumerable

{

// System.Array already implements IEnumerator!

private Car[] carArray;

public Garage()

{

carArray = new Car[4];

carArray[0] = new Car("FeeFee", 200, 0);

carArray[1] = new Car("Clunker", 90, 0);

carArray[2] = new Car("Zippy", 30, 0);

carArray[3] = new Car("Fred", 30, 0);

}

public IEnumerator GetEnumerator()

{

// Return the array object's IEnumerator.

return carArray.GetEnumerator();

}

}

Once you have updated your Garage type, you can now safely use the type within the C# foreach

construct.

// Hand over each car in the collection? // Work with IEnumerator indirectly. foreach (Car1 c in carLot)

{

Console.WriteLine("{0} is going {1} MPH",

c.PetName, c.CurrSpeed);

}

Furthermore, given that the GetEnumerator() method has been defined publicly, the object user

could also interact with the IEnumerator type:

// Manually work with IEnumerator.

IEnumerator i = carLot.GetEnumerator();

i.MoveNext();

Car myCar = (Car)i.Current;

Console.WriteLine("{0} is going {1} MPH", myCar.PetName, myCar.CurrSpeed);

Understanding C# Iterator Methods

If you wished to have your custom collections (such as Garage) support foreach like enumeration,

implementing the IEnumerable interface (and possibly the IEnumerator interface) was mandatory.

However, C# offers an alternative way to build types that work with the foreach loop via iterators.

Simply put, an iterator is a member that specifies how a container’s internal items should be

returned when processed by foreach. While the iterator method must still be named

GetEnumerator(), and the return value must still be of type IEnumerator, your custom class does not

need to implement any of the expected interfaces:

Page 12: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 12

Prof. Sushant S.Sundikar C# Programming

public class Garage // No longer implementing IEnumerable!

{

private Car[] carArray;

...

// Iterator method.

public IEnumerator GetEnumerator()

{

foreach (Car c in carArray)

{

yield return c;

}

}

}

An iterator is a method, get accessor, or operator that performs a custom iteration over an array or

collection class by using the yield keyword. The yield return statement causes an element in the

collection to be returned immediately to the caller function before the next element in the collection

is accessed. Although you write an iterator as a method, the compiler translates it into a nested class

within the scope of the defining type (Garage in this case). The autogenerated class implements the

GetEnumerator(), MoveNext() and Current members on your behalf This class keeps track of the

position of the iterator as long the foreach loop on the client code continues.

Building Cloneable Objects (ICloneable)

System.Object defines a member named MemberwiseClone(), which is used to obtain a shallow copy

of the current object. Object users do not call this method directly (as it is protected); however, a

given object may call this method itself during the cloning process. To illustrate, assume you have a

class named Point:

// A class named Point.

public class Point

{

// Public for easy access.

public int x, y;

public Point(int x, int y) { this.x = x; this.y = y;}

public Point(){}

// Override Object.ToString().

public override string ToString()

{ return string.Format("X = {0}; Y = {1}", x, y ); }

}

When you wish to equip your custom types to support the ability to return an identical copy of itself

to the caller, you may implement the standard ICloneable interface. This type defines a single

method named Clone():

public interface ICloneable

{

object Clone();

}

Page 13: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 13

Prof. Sushant S.Sundikar C# Programming

Obviously, the implementation of the Clone() method varies between objects. However, the basic

functionality tends to be the same: Copy the values of your member variables into a new object

instance, and return it to the user. To illustrate, ponder the following update to the Point class:

// The Point now supports "clone-ability." public class Point : ICloneable {

public int x, y;

public Point(){ }

public Point(int x, int y) { this.x = x; this.y = y;}

// Return a copy of the current object. public object Clone()

{

return new Point(this.x, this.y);

}

public override string ToString()

{

return string.Format("X = {0}; Y = {1}", x, y );

}

}

In this way, you can create exact stand-alone copies of the Point type, as illustrated by the following

code:

static void Main(string[] args)

{

// Notice Clone() returns a generic object type.

// You must perform an explicit cast to obtain the derived type.

Point p3 = new Point(100, 100);

Point p4 = (Point)p3.Clone();

// Change p4.x (which will not change p3.x).

p4.x = 0;

// Print each object.

Console.WriteLine(p3);

Console.WriteLine(p4);

}

Building IComparable Objects

If you want to sort objects that are stored in a non-generic collection, then you will implement the

non-generic version of IComparable. This version defines only one method, CompareTo( ), which

determines how comparisons are performed. The general form of CompareTo( ) is shown here:

int CompareTo(object obj)

CompareTo( ) compares the invoking object to obj. To sort in ascending order, your implementation

must return zero if the objects are equal, a positive value if the invoking object is greater than obj,

and a negative value if the invoking object is less than obj. You can sort in descending order by

Page 14: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 14

Prof. Sushant S.Sundikar C# Programming

reversing the outcome of the comparison. The method can throw an ArgumentException if the type

of obj is not compatible for comparison with the invoking object.

Here is an example that shows how to implement IComparable. It adds IComparable to the

Inventory class developed in the preceding section. It implements CompareTo( ) so that it compares

the name field, thus enabling the inventory to be sorted by name. By implementing IComparable, it

allows a collection of Inventory objects to be sorted, as the program illustrates.

// Implement IComparable.

using System;

using System.Collections;

// Implement the non-generic IComparable interface.

class Inventory : IComparable {

string name;

double cost;

int onhand;

public Inventory(string n, double c, int h) {

name = n;

cost = c;

onhand = h;

}

public override string ToString() {

return

String.Format("{0,-10}Cost: {1,6:C} On hand: {2}",

name, cost, onhand);

}

// Implement the IComparable interface.

public int CompareTo(object obj) {

Inventory b;

b = (Inventory) obj;

return name.CompareTo(b.name);

}

}

class IComparableDemo {

static void Main() {

ArrayList inv = new ArrayList();

// Add elements to the list.

inv.Add(new Inventory("Pliers", 5.95, 3));

inv.Add(new Inventory("Wrenches", 8.29, 2));

inv.Add(new Inventory("Hammers", 3.50, 4));

inv.Add(new Inventory("Drills", 19.88, 8));

Console.WriteLine("Inventory list before sorting:");

foreach(Inventory i in inv)

{

Console.WriteLine(" " + i.ToString());

Page 15: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 15

Prof. Sushant S.Sundikar C# Programming

}

Console.WriteLine();

// Sort the list.

inv.Sort();

Console.WriteLine("Inventory list after sorting:");

foreach(Inventory i in inv)

{

Console.WriteLine(" " + i);

}

}

}

Here is the output. Notice that after the call to Sort( ), the inventory is sorted by name.

Inventory list before sorting:

Pliers Cost: $5.95 On hand: 3

Wrenches Cost: $8.29 On hand: 2

Hammers Cost: $3.50 On hand: 4

Drills Cost: $19.88 On hand: 8

Inventory list after sorting:

Drills Cost: $19.88 On hand: 8

Hammers Cost: $3.50 On hand: 4

Pliers Cost: $5.95 On hand: 3

Wrenches Cost: $8.29 On hand: 2

The Interfaces of the System.Collections Namespace

System.Collections Interface Meaning in Life

ICollection Defines generic characteristics (e.g., count and thread safety) for a

collection type.

IEqualityComparer Defines methods to support the comparison of objects for equality.

IDictionary Allows an object to represent its contents using name/value pairs.

IDictionaryEnumerator Enumerates the contents of a type supporting IDictionary.

IEnumerable Returns the IEnumerator interface for a given object.

IEnumerator Generally supports foreach-style iteration of subtypes.

IHashCodeProvider Returns the hash code for the implementing type using a customized

hash algorithm.

IKeyComparer Combines the functionality of IComparer and IHashCodeProvider to

allow objects to be compared in a “hash-code-compatible manner” (e.g.,

if the objects are indeed equal, they must also return the same hash

code value).

IList Provides behavior to add, remove, and index items in a list of objects.

Also, this interface defines members to determine whether the

implementing collection type is read-only and/or a fixed-size container.

Understanding the .NET Delegate Type

Historically speaking, the Windows API makes frequent use of C-style function pointers to create

entities termed callback functions or simply callbacks. Using callbacks, programmers were able to

configure one function tocall back another function in the application .

Page 16: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 16

Prof. Sushant S.Sundikar C# Programming

In the .NET Framework, callbacks are still possible, and their functionality is accomplished in a much

safer and more object-oriented manner using delegates.A delegate type maintains three important

pieces of information:

• The name of the method on which it makes calls

• The arguments (if any) of this method

• The return value (if any) of this method

Defining a Delegate

A delegate is an object that can refer to a method. Therefore, when you create a delegate, you are

creating an object that can hold a reference to a method. Furthermore, the method can be called

through this reference. In other words, a delegate can invoke the method to which it refers.

You create a delegate with the delegate keyword, followed by a return type and the signature of the

methods that can be delegated to it, as in the following syntax:

access-modifier delegate return-type Delegate_Name(Parameter-List);

For example, assume you wish to build a delegate named BinaryOp that can point to any method

that returns an integer and takes two integers as input parameters:

// This delegate can point to any method, taking two integers and returning an integer.

public delegate int BinaryOp(int x, int y);

When the C# compiler processes delegate types, it automatically generates a sealed class deriving

from System.MulticastDelegate. This class provides the necessary infrastructure for the delegate to

hold onto the list of methods to be invoked at a later time.

As you can see, the generated BinaryOp class defines three public methods. Invoke() is perhaps the

core method, as it is used to invoke each method maintained by the delegate type in a synchronous

manner, meaning the caller must wait for the call to complete before continuing on its way.

BeginInvoke() and EndInvoke() provide the ability to call the current method asynchronously on a

second thread of execution.

Page 17: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 17

Prof. Sushant S.Sundikar C# Programming

Now, how exactly does the compiler know how to define the Invoke(), BeginInvoke(), and

EndInvoke() methods? To understand the process, here is the crux of the generated BinaryOp class

type (bold marks the items specified by the defined delegate type):

sealed class BinaryOp : System.MulticastDelegate {

public BinaryOp(object target, uint functionAddress); public int Invoke(int x, int y); public IAsyncResult BeginInvoke(int x, int y, AsyncCallback cb, object state);

public int EndInvoke(IAsyncResult result);

}

First, notice that the parameters and return value defined for the Invoke() method exactly match the

definition of the BinaryOp delegate. The initial parameters to BeginInvoke() members (two integers

in our case) are also based on the BinaryOp delegate. Finally, the return value of EndInvoke() is

identical to the original delegate declaration.

To summarize, a C# delegate definition results in a sealed class with three compiler-generated

methods whose parameter and return types are based on the delegate’s declaration.

The System.MulticastDelegate and System. Delegate Base Classes

So, when you build a type using the C# delegate keyword, you indirectly declare a class type that

derives from System.MulticastDelegate. This class provides descendents with access to a list that

contains the addresses of the methods maintained by the delegate type, as well as several additional

methods (and a few overloaded operators) to interact with the invocation list. Here are the Select

Members of System.MultcastDelegate/System.Delegate

Inherited Member Meaning in Life

Method This property returns a System.Reflection.MethodInfo type

thatrepresents details of a static method that is maintained by the

delegate.

Target If the method to be called is defined at the object level (rather than a

staticmethod), Target returns an object that represents the method

maintained by the delegate. If the value returned from Target equals

null, the method to be called is a static member.

Combine() This static method adds a method to the list maintained by the

delegate.In C#, you trigger this method using the overloaded +=

operator as a shorthand notation.

GetInvocationList() This method returns an array of System.Delegate types, each

representing a particular method that may be invoked.

Remove() These static methods removes a method (or all methods) from the

RemoveAll() invocation list. In C#, the Remove() method can be called indirectly

usingthe overloaded -= operator.

Page 18: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 18

Prof. Sushant S.Sundikar C# Programming

The Simplest Possible Delegate Example

Program ch04pg06.cs: Program to demonstrate use of delegates

using System;

// This delegate can point to any method,

// taking two integers and returning an

// integer.

public delegate int BinaryOp(int x, int y);

// This class contains methods BinaryOp will

// point to.

public class SimpleMath

{

public static int Add(int x, int y)

{ return x + y; }

public static int Subtract(int x, int y)

{ return x – y; }

}

class Program

{

static void Main(string[] args)

{

Console.WriteLine("***** Simple Delegate Example *****\n");

// Create a BinaryOp object that

// "points to" SimpleMath.Add().

BinaryOp b = new BinaryOp(SimpleMath.Add);

b+=SimpleMath.Sub;

// Invoke Add() method using delegate.

Console.WriteLine("10 + 10 is {0}", b(10, 10));

Console.ReadLine();

}

}

Multicasting

One of the most exciting features of a delegate is its support for multicasting. In simple terms,

multicasting is the ability to create an invocation list, or chain, of methods that will be automatically

called when a delegate is invoked. Such a chain is very easy to create. Simply instantiate a delegate,

and then use the + or += operator to add methods to the chain. To remove a method, use – or – =.

If the delegate returns a value, then the value returned by the last method in the list becomes the

return value of the entire delegate invocation. Thus, a delegate that makes use of multicasting will

often have a void return type.

Program ch04pg07.cs: Program to demonstrate multicasting of delegates

using System;

// Define a custom delegate that has a string parameter and returns void.

Page 19: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 19

Prof. Sushant S.Sundikar C# Programming

delegate void CustomDel(string s);

class TestClass

{

// Define two methods that have the same signature as CustomDel.

static void Hello(string s)

{

System.Console.WriteLine(" Hello, {0}!", s);

}

static void Goodbye(string s)

{

System.Console.WriteLine(" Goodbye, {0}!", s);

}

static void Main()

{

// Declare instances of the custom delegate.

CustomDel hiDel, byeDel, multiDel, multiMinusHiDel;

// Create the delegate object hiDel that references the

// method Hello.

hiDel = Hello;

// Create the delegate object byeDel that references the

// method Goodbye.

byeDel = Goodbye;

// The two delegates, hiDel and byeDel, are combined to

// form multiDel.

multiDel = hiDel + byeDel;

// Remove hiDel from the multicast delegate, leaving byeDel,

// which calls only the method Goodbye.

multiMinusHiDel = multiDel - hiDel;

Console.WriteLine("Invoking delegate hiDel:");

hiDel("A");

Console.WriteLine("Invoking delegate byeDel:");

byeDel("B");

Console.WriteLine("Invoking delegate multiDel:");

multiDel("C");

Console.WriteLine("Invoking delegate multiMinusHiDel:");

multiMinusHiDel("D");

}

}

/* Output:

Invoking delegate hiDel:

Hello, A!

Invoking delegate byeDel:

Goodbye, B!

Invoking delegate multiDel:

Hello, C!

Goodbye, C!

Invoking delegate multiMinusHiDel:

Goodbye, D!

*/

Page 20: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 20

Prof. Sushant S.Sundikar C# Programming

Understanding Events in C#

Another important C# feature is built upon the foundation of delegates: the event. An event is,

essentially, an automatic notification that some action has occurred. Events work like this: An object

that has an interest in an event registers an event handler for that event. When the event occurs, all

registered handlers are called. Event handlers are represented by delegates.

Events are members of a class and are declared using the event keyword. Its most commonly used

form is shown here:

event event-delegate event-name;

Here, event-delegate is the name of the delegate used to support the event, and event-

name is the name of the specific event object being declared. Let’s begin with a very simple example:

Program ch04pg08.cs: A very simple event demonstration.

using System;

// Declare a delegate type for an event.

delegate void MyEventHandler();

// Declare a class that contains an event.

class MyEvent {

public event MyEventHandler SomeEvent;

// This is called to fire the event.

public void OnSomeEvent() {

if(SomeEvent != null)

SomeEvent();

}

}

class EventDemo {

// An event handler.

static void Handler() {

Console.WriteLine("Event occurred");

}

static void Main() {

Page 21: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 21

Prof. Sushant S.Sundikar C# Programming

MyEvent evt = new MyEvent();

// Add Handler() to the event list.

evt.SomeEvent += Handler;

// Fire the event.

evt.OnSomeEvent();

}

}

This program displays the following output:

Event occurred

The program begins by declaring a delegate type for the event handler, as shown here:

delegate void MyEventHandler();

All events are activated through a delegate. Thus, the event delegate type defines the return type

and signature for the event. Next, an event class, called MyEvent, is created. Inside the class, an

event called SomeEvent is declared, using this line:

public event MyEventHandler SomeEvent;

The keyword event tells the compiler that an event is being declared. Also declared inside MyEvent

is the method OnSomeEvent( ), which is the method a program will call to signal (or “fire”) an event.

(That is, this is the method called when the event occurs.) It calls an event handler through the

SomeEvent delegate, as shown here:

if(SomeEvent != null)

SomeEvent();

Notice that a handler is called if and only if SomeEvent is not null. Since other parts of your program

must register an interest in an event in order to receive event notifications, it is possible that

OnSomeEvent( ) could be called before any event handler has been registered. To prevent calling on

a null reference, the event delegate must be tested to ensure that it is not null.

Inside EventDemo, an event handler called Handler() is created. In this simple example, the event

handler simply displays a message, but other handlers could perform more meaningful actions. In

Main( ), a MyEvent object is created, and Handler() is registered as a handler for this event, by

adding it as shown here:

MyEvent evt = new MyEvent();

// Add Handler() to the event list.

evt.SomeEvent += Handler;

Page 22: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 22

Prof. Sushant S.Sundikar C# Programming

Notice that the handler is added using the += operator. Events support only += and – =. In this case,

Handler( ) is a static method, but event handlers can also be instance methods. Finally, the event is

fired as shown here:

// Fire the event.

evt.OnSomeEvent();

Calling OnSomeEvent( ) causes all registered event handlers to be called.

Anonymous Methods

An anonymous method is one way to create an unnamed block of code that is associated with a

specific delegate instance. An anonymous method is created by following the keyword delegate with

a block of code. To see how this is done, let’s begin with a simple example. The following program

uses an anonymous method that counts from 0 to 5.

Program ch04pg10.cs : Program to demonstrate anonymous method

// Demonstrate an anonymous method.

using System;

// Declare a delegate type.

delegate void CountIt();

class AnonMethDemo {

static void Main() {

// Here, the code for counting is passed

// as an anonymous method.

CountIt count = delegate {

// This is the block of code passed to the delegate.

for(int i=0; i <= 5; i++)

Console.WriteLine(i);

}; // notice the semicolon

count();

}

}

This program first declares a delegate type called CountIt that has no parameters and returns void.

Inside Main( ), a CountIt instance called count is created, and it is passed the block of code that

follows the delegate keyword. This block of code is the anonymous method that will be executed

when count is called. Notice that the block of code is followed by a semicolon, which terminates the

declaration statement. The output from the program is shown here:

0

1

2

3

Page 23: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 23

Prof. Sushant S.Sundikar C# Programming

4

5

Method Group Conversion Beginning with version 2.0, C# has included an option that significantly simplifies the syntax that

assigns a method to a delegate. This feature is called method group conversion, and it allows you to

simply assign the name of a method to a delegate, without using new or explicitly invoking the

delegate’s constructor.

For example, here is the Main( ) method of the preceding program rewritten to use method group

conversions:

Program ch04pg11.cs: Program to demonstrate method group conversion.

// A simple method group conversion example.

using System;

// Declare a delegate type.

delegate string StrMod(string str);

class DelegateTest

{

// Replaces spaces with hyphens.

static string ReplaceSpaces(string s)

{

Console.WriteLine("Replacing spaces with hyphens.");

return s.Replace(' ', '-');

}

static void Main()

{

// Construct a delegate using method group conversion.

StrMod strOp = ReplaceSpaces; // use method group

// conversion

string str;

// Call methods through the delegate.

str = strOp("This is a test.");

Console.WriteLine("Resulting string: " + str);

Console.WriteLine();

}

}

Pay special attention to the way that strOp is created and assigned the method ReplaceSpaces in

this line:

Page 24: Unit 4: Interfaces, Collections, Delegates & Events · 2013. 9. 19. · Unit 4: Interfaces, Collections, Delegates & Events 10 Prof. Sushant S.Sundikar C# Programming carArray[3]

Unit 4: Interfaces, Collections, Delegates & Events 24

Prof. Sushant S.Sundikar C# Programming

StrMod strOp = ReplaceSpaces; // use method group conversion

The name of the method is assigned directly to strOp. C# automatically provides a conversion from

the method to the delegate type. This syntax can be generalized to any situation in which a method

is assigned to (or converted to) a delegate type.

Program of Unit 4

// Program Ch04pg01.cs

//Program to demonstrate the use of interface

using System;

public interface MyInterface

{

void showString();

int add(int a, int b);

}

class MyClass : MyInterface

{

public void showString()

{

Console.WriteLine("Interface Method Implemented");

}

public int add(int a,int b)

{

return a+b;

}

}

class InterfaceDemo

{

public static void Main()

{

MyClass mc=new MyClass();

mc.showString();

int result=mc.add(5,7);

Console.WriteLine("Sum of 5 and 7 is :"+result);

}

}