interfaces and collections · 1 interfaces and collections defining interfaces in c# • an...

29
1 Interfaces and Collections Defining Interfaces in C# An interface is 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. At a syntactic level, an interface is defined using the C# interface keyword. Unlike other .NET types, interfaces never specify a base class (not even System.Object) and contain members that do not take an access modifier (as all interface members are implicitly public). public interface IPointy { // Implicitly public and abstract. byte GetNumberOfPoints(); } As you can see, the IPointy interface defines a single method. However, .NET interface types are also able to define any number of properties. For example, you could create the IPointy interface to use a read-only property rather than a traditional accessor method: public interface IPointy { byte Points{get;} } Can’t create instance for an interface: static void Main(string[] args) { IPointy p = new IPointy(); // Compiler error! } Interfaces do not bring much to the table until they are implemented by a class or structure. Implementing an Interface in C# When a class (or structure) chooses to extend its functionality by supporting interface types, it does so using a comma-delimited list in the type definition. The direct base class must be the first item listed after the colon operator. When your class type derives directly from System.Object, you are free to simply list the interface(s) supported by the class, as the C# compiler will extend your types from System.Object if you do not say otherwise. structures always derive from System.ValueType, simply list each interface directly after the structure definition. // This class derives from System.Object and implements a single interface. public class SomeClass : ISomeInterface {...} // This class also derives from System.Object and implements a single interface. public class MyClass : object, ISomeInterface {...} // This class derives from a custom base class and implements a single interface. public class AnotherClass : MyBaseClass, ISomeInterface {...} // This struct derives from System.ValueType and implements two interfaces.

Upload: others

Post on 16-Aug-2021

8 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

1

Interfaces and Collections

Defining Interfaces in C#

• An interface is 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.

• At a syntactic level, an interface is defined using the C# interface keyword.

• Unlike other .NET types, interfaces never specify a base class (not even System.Object) and contain members

that do not take an access modifier (as all interface members are implicitly public).

public interface IPointy

{

// Implicitly public and abstract.

byte GetNumberOfPoints();

}

• As you can see, the IPointy interface defines a single method. However, .NET interface types are also able to

define any number of properties.

• For example, you could create the IPointy interface to use a read-only property rather than a traditional

accessor method:

public interface IPointy

{

byte Points{get;}

}

• Can’t create instance for an interface:

static void Main(string[] args)

{

IPointy p = new IPointy(); // Compiler error!

}

• Interfaces do not bring much to the table until they are implemented by a class or structure.

Implementing an Interface in C#

• When a class (or structure) chooses to extend its functionality by supporting interface types, it does so using

a comma-delimited list in the type definition.

• The direct base class must be the first item listed after the colon operator.

• When your class type derives directly from System.Object, you are free to simply list the interface(s) supported

by the class, as the C# compiler will extend your types from System.Object if you do not say otherwise.

• structures always derive from System.ValueType, simply list each interface directly after the structure

definition.

// This class derives from System.Object and implements a single interface.

public class SomeClass : ISomeInterface

{...}

// This class also derives from System.Object and implements a single interface.

public class MyClass : object, ISomeInterface

{...}

// This class derives from a custom base class and implements a single interface.

public class AnotherClass : MyBaseClass, ISomeInterface

{...}

// This struct derives from System.ValueType and implements two interfaces.

Page 2: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

2 public struct SomeStruct : ISomeInterface, IPointy

{...}

• If you are implementing an interface that defines ten members, the type is now responsible for fleshing out

the details of the ten abstract entities.

// 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; }

}

}

// New Shape derived class named Triangle.

public class Triangle : Shape, IPointy

{

public Triangle() { }

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

public override void Draw()

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

// IPointy Implementation.

public byte Points

{

get { return 3; }

}

}

• Each class now returns its number of points to the caller when asked to do so.

• Contrasting Interfaces to Abstract Base Classes

• c# allows you to build abstract class types containing abstract methods.

• LikeLike an interface, when a class derives from an abstract base class, it is also under obligation to flesh out

the details of the abstract methods

• Abstract base classes 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.

Page 3: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

3

• 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!"); }

}

• 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.

IS and AS keyword

Look at the example given below:

1. Circle c = new Circle(32); 2. object o = c; 3. int i = (int)o; // it compiles okay but throws an exception at runtime

• if the type of object in memory does not match the cast, the runtime will throw an

InvalidCastException.

• We should be prepared to catch this exception and handle it appropriately if it occurs.

• However, catching an exception and attempting to recover in the event that the type of an object is

not what we expected it to be is a rather cumbersome approach.

• C# provides two more very useful operators that can help us to perform casting in a much more

elegant manner by using the "is" and "as" operators.

is Operator • The "is" operator is used to check whether the run-time type of an object is compatible with a given

type or not.

• In other words, we use the "is" operator to verify that the type of an object is what we expect it to be.

syntax:

expression is type

Example of the "is" operator:

using System; class Class1 { } class Class2 { } public class IsTest

{ public static void Test(object o) { Class1 a; Class2 b;

Page 4: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

4 if (o is Class1) { Console.WriteLine("o is Class1"); a = (Class1)o; }

else if (o is Class2) { Console.WriteLine("o is Class2"); b = (Class2)o; } else { Console.WriteLine("o is neither Class1 nor Class2."); } } public static void Main() { Class1 c1 = new Class1(); Class2 c2 = new Class2(); Test(c1); Test(c2);

Test("Passing String Value instead of class"); Console.ReadKey(); } }

• In the above example, I'll be checking whether object o is a class or not.

• If the argument passed is not a class then the application will jump to the message 'o is neither

class1 nor class2'.

as Operator

• The "as" operator is used to perform conversions between compatible types.

• Actually, the "as" operator fulfills a similar role like "is" but in a slightly truncated manner.

syntax:

expression as type

Example of the "as" operator:

using System; class Class1 { } class Class2 { } public class IsTest { public static void Main() {

Page 5: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

5 object[] myObjects = new object[6]; myObjects[0] = new Class1(); myObjects[1] = new Class2(); myObjects[2] = "string"; myObjects[3] = 32;

myObjects[4] = null; for (int i = 0; i < myObjects.Length; ++i) { string s = myObjects[i] as string; Console.Write("{0}:", i); if (s != null) Console.WriteLine("'" + s + "'"); else Console.WriteLine("not a string"); } Console.ReadKey(); } }

In the above example, each and every value is being cast to a string using the "as" operator and assigned

to a string variable which is shown on the console.

Invoking Interface Members at the Object Level

• 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();

}

• This approach works fine in this particular case, given that you are well aware that the Hexagon type has

implemented the interface in question.

• Other times, 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.

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

cast.Ifthe type does not supportthe requested interface, you receive an InvalidCastException.

• To handle this possibility gracefully, make use of structured exception handling, for example:

static void Main(string[] args)

Page 6: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

6 {

...

// 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); }

Console.ReadLine();

}

• While you could make use of try/catch logic and hope for the best, it would be ideal to determine which

interfaces are supported before invoking the interface members in the first place.

• Let’s see two ways of doing so.

Obtaining Interface References: The as Keyword

• 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:

static void Main(string[] args)

{

...

// 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...");

}

• Obtaining Interface References: The is Keyword

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

• If the object 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, you can safely call the members without

needing to make use of try/catch logic.

static void Main(string[] args)

{

...

Shape[] s = { new Hexagon(), new Circle(), new Triangle("Joe"),new Circle("JoJo")} ;

faces

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

{

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

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

s[i].Draw();

Page 7: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

7 if(s[i] is IPointy)

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

else

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

}

}

The output follows in Figure 7-2.

Interfaces As Parameters

• Interfaces are valid .NET types, you may construct methods that take interfaces as parameters.

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

public interface IDraw3D

{

void Draw3D();

}

// Circle supports IDraw3D.

public class Circle : Shape, IDraw3D

{

...

public void Draw3D()

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

}

// Hexagon supports IPointy and IDraw3D.

public class Hexagon : Shape, IPointy, IDraw3D

{

...

public void Draw3D()

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

}

public class Program

{

// I'll draw anyone supporting IDraw3D.

public static void DrawIn3D(IDraw3D itf3d)

{

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

itf3d.Draw3D();

}

static void Main()

{

Page 8: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

8 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.

// This method tests for IPointy-compatibility and,

// if able, returns an interface reference.

static IPointy ExtractPointyness(object o)

{

if (o is IPointy)

return (IPointy)o;

else

return null;

}

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

• Same interface can be implemented by numerous types, even if they are not within the same class hierarchy.

• This can yield some very powerful programming constructs.

• For example, assume that you have developed a brand new class hierarchy modeling kitchen utensils and

another modeling gardening equipment.

• Although these hierarchies are completely unrelated from a classical inheritance point of view, you can treat

them polymorphically using interface-based programming.

• To illustrate, assume you have an array of IPointy-compatible objects. Given that these members all support

the same interface, you are able to iterate through the array and treat each object as an IPointy-compatible

object, regardless of the overall diversity of the class hierarchies:

static void Main(string[] args)

{

// This array can only contain types that

// implement the IPointy interface.

IPointy[] myPointyObjects = {new Hexagon(), new Knife(),

Page 9: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

9 Bbnew Triangle(), new Fork(), new PitchFork()};

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

Console.WriteLine("Object has {0} points.", myPointyObjects[i].Points);

}

Explicit Interface Implementation

• By using Explicit interface implementation technique, you are able to ensure that the object user can only

access methods defined by a given interface using the correct interface reference, as well as circumvent

possible name clashes.

// Using explicit method implementation we are able

// to provide distinct Draw() implementations.

public class Line : Shape, IDraw3D

{

// You can only call this method from an IDraw3D interface reference.

void IDraw3D.Draw()

{ Console.WriteLine("Drawing a 3D line..."); }

// You can only call this at the object level.

public override void Draw()

{ Console.WriteLine("Drawing a line..."); }

}

• As you can see, when explicitly implementing an interface member, the general pattern breaks down to

returnValue InterfaceName.MethodName(args).

• There are a few odds and ends to be aware of when using explicit interface implementation. First and

foremost, you cannot define the explicitly implemented members with an access modifier. For example, the

following is illegal syntax:

// Nope! Illegal.

public class Line : Shape, IDraw3D

{

public void IDraw3D.Draw() // <= Error!

{

Console.WriteLine("Drawing a 3D line...");

}

...

}

• The reason to use explicit interface method implementation is to ensure that a given interface method is

bound at the interface level.

• If you were to add the public keyword, this would suggest that the method is a member of the public sector

of the class, which defeats the point! Given this design, the caller is only able to invoke the Draw() method

defined by the Shape base class from the object level:

// This invokes the overridden Shape.Draw() method.

Line myLine = new Line();

myLine.Draw();

• To invoke the Draw() method defined by IDraw3D, we must now explicitly obtain the interface reference using

any of the techniques shown previously.

• For example:

// This triggers the IDraw3D.Draw() method.

Line myLine = new Line();

Page 10: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

10 IDraw3D i3d = (IDraw3D)myLine;

i3d.Draw();

Resolving Name Clashes

• Explicit interface implementation can also be very helpful whenever you are implementing a number of

interfaces that happen to contain identical members.

• For example, assume you wish to create a class that implements all the following new interface types:

// Three interfaces each define identically named methods.

public interface IDraw

{

void Draw();

}

public interface IDrawToPrinter

{

void Draw();

}

public class SuperImage : IDraw, IDrawToPrinter, IDraw3D

{

void IDraw.Draw()

{ /* Basic drawing logic. */ }

void IDrawToPrinter.Draw()

{ /* Printer logic. */ }

void IDraw3D.Draw()

{ /* 3D rendering logic. */ }

}

Building 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.

• 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();}

public interface IPrintable : IDrawable

{ void Print(); }

public interface IMetaFileRender : IPrintable

{ void Render(); }

Figure 7-5 illustrates the chain of inheritance.

Page 11: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

11

• Any methods defined by the baseinterface(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()

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

}

// Exercise the interfaces.

static void Main(string[] args)

{

SuperImage si = new SuperImage();

// Get IDrawable.

IDrawable itfDraw = (IDrawable)si;

itfDraw.Draw();

// Now get ImetaFileRender, which exposes all methods up

// the chain of inheritance.

if (itfDraw is IMetaFileRender)

{

IMetaFileRender itfMF = (IMetaFileRender)itfDraw;

itfMF.Render();

itfMF.Print();

}

Console.ReadLine();

}

Interfaces with Multiple Base Interfaces

• You can create an interface that derives from multiple base interfaces.

• Example:

public interface ICar

Page 12: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

12 { void Drive(); }

public interface IUnderwaterCar

{ void Dive(); }

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

public interface IJamesBondCar : ICar, IUnderwaterCar

{ void TurboBoost(); }

Figure illustrates the chain of inheritance.

If you were to build a class that implements IJamesBondCar, you would now be responsible for implementing

TurboBoost(), Dive(), and Drive():

public class JamesBondCar : IJamesBondCar

{

public void Drive(){ Console.WriteLine("Speeding up...");}

public void Dive(){ Console.WriteLine("Submerging...");}

public void TurboBoost(){ Console.WriteLine("Blast off!");}

}

static void Main(string[] args)

{

...

JamesBondCar j = new JamesBondCar();

j.Drive();

j.TurboBoost();

j.Dive();

}

Implementing Interfaces Using Visual Studio 2005

Refer the text book page number 234, 235

Building Enumerable Types (IEnumerable and IEnumerator)

• An IEnumerator is a thing that can enumerate: it has the Current property and the MoveNext

and Reset methods (which in . NET code you probably won't call explicitly, though you could).

• An IEnumerable is a thing that can be enumerated...which simply means that it has a

GetEnumerator method that returns an IEnumerator

• GetEnumerator() method is formalized by the IEnumerable interface, which is found lurking within

the System.Collections namespace.

• Objects that support this behavior advertise that they are able to expose contained subitems to the

caller:

// This interface informs the caller

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

public interface IEnumerable

{

IEnumerator GetEnumerator();

Page 13: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

13

}

• 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.

• While you are certainly free to provide customized versions of GetEnumerator(), MoveNext(),

Current, and Reset(), there is a simpler way.

• As the System.Array type (as well as many other types) already implements IEnumerable and

IEnumerator, you can simply delegate the request to the System.Array as follows:

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.

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

could also interact with the IEnumerator type:

Page 14: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

14

// 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);

• If you would prefer to hide the functionality of IEnumerable from the object level, simply make use

of explicit interface implementation:

public IEnumerator IEnumerable.GetEnumerator()

{

// Return the array object's IEnumerator.

return carArray.GetEnumerator();

}

Understanding C# Iterator Methods

• 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:

public class Garage // No longer implementing IEnumerable!

{

private Car[] carArray;

...

// Iterator method.

public IEnumerator GetEnumerator()

{

foreach (Car c in carArray)

{

yield return c;

}

}

}

• Notice that this implementation of GetEnumerator() iterates over the subitems using internal foreach

logic and returns each Car to the caller using the new yield return syntax.

• The yield keyword is used to specify the value (or values) to be returned to the caller’s foreach

construct.

• When the yield return statement is reached, the current location is stored, and execution is restarted

from this location the next time the iterator is called.

• When the C# compiler encounters an iterator method, it will dynamically generate 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 (oddly, the Reset() method is

not, and you will receive a runtime exception if you attempt to call it).

Page 15: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

15

If you were to load the current application into ildasm.exe, you would find that the Garage’s implementation

of GetEnumerator() is making use of this compiler-generated type internally:

.method public hidebysig instance class [mscorlib]System.Collections.IEnumerator GetEnumerator() cil

managed

{

...

newobj instance void

CustomEnumeratorWithYield.Garage/'<GetEnumerator>d__0'::.ctor(int32)

...

} // end of method Garage::GetEnumerator

• However, if you are building a more exotic custom container (such as a binary tree) where you need

to manually implement the IEnumerator and IEnumerable interfaces, the C# iterator syntax can be

a massive time-saver. In any case, the caller’s code is identical when interacting with a type’s iterator

method via foreach:

static void Main(string[] args)

{

Console.WriteLine("***** Fun with Iterator Methods *****\n");

Garage carLot = new Garage();

foreach (Car c in carLot)

{

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

}

Console.ReadLine();

}

Building Cloneable Objects (ICloneable)

• ICloneable interface contains one member, Clone, which is intended to support cloning beyond that supplied by MemberwiseClone.

• It is a procedure that can create a true, distinct copy of an object and all its dependent object, is to rely on the serialization features of the .NET framework.

There are two ways to clone an instance:

An instance is an actual object created to the specification defined by a class.

1. Shallow copy - may be linked to data shared by both the original and the copy

2. Deep copy - contains the complete encapsulated data of the original object

Syntex

[ComVisibleAttribute(true)]

Page 16: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

16

public interface ICloneable

• System.Object defines a member named MemberwiseClone(). This method 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 ); }

}

static void Main(string[] args)

{

// Two references to same object!

Point p1 = new Point(50, 50);

Point p2 = p1;

p2.x = 0;

Console.WriteLine(p1);

Console.WriteLine(p2);

}

• 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();

}

• 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

objectinstance, 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;}

Page 17: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

17

// 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);

}

• Because the Point type does not contain reference type variables, you could simplify the

implementation of the Clone() method as follows:

public object Clone()

{

// Copy each field of the Point member by member.

return this.MemberwiseClone();

}

A More Elaborate Cloning Example

// This class describes a point.

public class PointDescription

{

// Exposed publicly for simplicity.

public string petName;

public Guid pointID;

public PointDescription()

{

this.petName = "No-name";

pointID = Guid.NewGuid();

}

}

public class Point : ICloneable

{

public int x, y;

Page 18: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

18

public PointDescription desc = new PointDescription();

public Point(){}

public Point(int x, int y)

{

this.x = x; this.y = y;

}

public Point(int x, int y, string petname)

{

this.x = x;

this.y = y;

desc.petName = petname;

}

public object Clone()

{ return this.MemberwiseClone(); }

public override string ToString()

{

return string.Format("X = {0}; Y = {1}; Name = {2};\nID = {3}\n",

x, y, desc.petName, desc.pointID);

}

}

static void Main(string[] args)

{

Console.WriteLine("Cloned p3 and stored new Point in p4");

Point p3 = new Point(100, 100, "Jane");

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

Console.WriteLine("Before modification:");

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

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

p4.desc.petName = "Mr. X";

p4.x = 9;

Console.WriteLine("\nChanged p4.desc.petName and p4.x");

Console.WriteLine("After modification:");

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

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

}

• In order for your Clone() method to make a complete deep copy of the internal reference types, you

need to configure the object returned by MemberwiseClone() to account for the current point’s

name (the System.Guid type is in fact a structure, so the numerical data is indeed copied). Here is

one possible implementation:

// Now we need to adjust for the PointDescription member.

public object Clone()

{

Point newPoint = (Point)this.MemberwiseClone();

PointDescription currentDesc = new PointDescription();

currentDesc.petName = this.desc.petName;

newPoint.desc = currentDesc;

Page 19: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

19

return newPoint;

}

Building Comparable Objects (IComparable)

• The System.IComparable interface specifies a behavior that allows an object to be sorted based on

some specified key. Here is the formal definition:

// This interface allows an object to specify its

// relationship between other like objects.

public interface IComparable

{

int CompareTo(object o);

}

Example:

public class Car

{

...

private int carID;

public int ID

{

get { return carID; }

set { carID = value; }

}

public Car(string name, int currSp, int id)

{

currSpeed = currSp;

petName = name;

carID = id;

}

...

}

static void Main(string[] args)

{

// Make an array of Car types.

Car[] myAutos = new Car[5];

myAutos[0] = new Car("Rusty", 80, 1);

myAutos[1] = new Car("Mary", 40, 234);

myAutos[2] = new Car("Viper", 40, 34);

myAutos[3] = new Car("Mel", 40, 4);

myAutos[4] = new Car("Chucky", 40, 5);

}

CompareTo()

Page 20: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

20

// The iteration of the Car can be ordered

// based on the CarID.

public class Car : IComparable

{

...

// IComparable implementation.

int IComparable.CompareTo(object obj)

{

Car temp = (Car)obj;

if(this.carID > temp.carID)

return 1;

if(this.carID < temp.carID)

return -1;

else

return 0;

}

}

static void Main(string[] args)

{

// Make an array of Car types.

...

// Dump current array.

Console.WriteLine("Here is the unordered set of cars:");

foreach(Car c in myAutos)

Console.WriteLine("{0} {1}", c.ID, c.PetName);

// Now, sort them using IComparable!

Array.Sort(myAutos);

// Dump sorted array.

Console.WriteLine("Here is the ordered set of cars:");

foreach(Car c in myAutos)

Console.WriteLine("{0} {1}", c.ID, c.PetName);

• Console.ReadLine();

}

Output:

Page 21: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

21

Specifying Multiple Sort Orders (IComparer)

• CompareTo method from IComparable interface can sort on only one field at a time, so

sorting on different properties with it is not possible.

• IComparer interface provides Compare method that Compares two objects and returns a

value indicating whether one is less than, equal to, or greater than the other.

A class that implements the IComparer interface must provide a Compare method that

compares two objects.

// A generic way to compare two objects.

interface IComparer

{

int Compare(object o1, object o2);

}

• to allow the object user to sort an array of Car types by pet name will require an additional helper

class that implements IComparer.

Here’s the code:

Page 22: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

22

// This helper class is used to sort an array of Cars by pet name.

using System.Collections;

public class PetNameComparer : IComparer

{

public PetNameComparer(){ }

// Test the pet name of each object.

int IComparer.Compare(object o1, object o2)

{

Car t1 = (Car)o1;

Car t2 = (Car)o2;

return String.Compare(t1.PetName, t2.PetName);

}

}

• The object user code is able to make use of this helper class. System.Array has a number of

overloaded Sort() methods, one that just happens to take an object implementing IComparer (see

Figure 7-11)

static void Main(string[] args)

{

...

// Now sort by pet name.

Array.Sort(myAutos, new PetNameComparer());

// Dump sorted array.

Console.WriteLine("Ordering by pet name:");

foreach(Car c in myAutos)

Console.WriteLine("{0} {1}", c.ID, c.PetName);

...

}

Custom Properties, Custom Sort Types

• It is worth pointing out that you can make use of a custom static property in order to help the object

user along when sorting your Car types by a specific data point. Assume the Car class has added a

Page 23: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

23

static read-only property named SortByPetName that returns an instance of an object implementing

the IComparer interface (PetNameComparer, in this case):

// We now support a custom property to return

// the correct IComparer interface.

public class Car : IComparable

{

...

// Property to return the SortByPetName comparer.

public static IComparer SortByPetName

{ get { return (IComparer)new PetNameComparer(); } }

}

• The object user code can now sort by pet name using a strongly associated property, rather than just

“having to know” to use the stand-alone PetNameComparer class type:

// Sorting by pet name made a bit cleaner.

Array.Sort(myAutos, Car.SortByPetName);

The Interfaces of the System.Collections Namespace

• The System.Collections namespace defines a number of interfaces.

• a majority of the collection classes implement these interfaces to provide access to their contents.

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

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

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

4. IDictionaryEnumerator: IDictionaryEnumerator Enumerates the contents of a type supporting

IDictionary.

5. IEnumerable: IEnumerable Returns the IEnumerator interface for a given object.

IEnumeratorIEnumerator Generally supports foreach-style iteration of subtypes.

6. IHashCodeProvider: Returns the hash code for the implementing type using a customized hash

algorithm.

7. IKeyComparer: IKeyComparer (This interface is new to .NET 2.0.) Combines the functionality of

IComparer and IHashCodeProvider to allow objects to be compared in a “hash-code-compatible

manner”

8. 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.

• Many of these interfaces are related by an interface hierarchy, while others are stand-alone entities.

• Figure illustrates the relationship between each type.

Page 24: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

24

ICollection

• The ICollection interface is the most primitive interface of the System.Collections namespace in that

it defines a behavior supported by a collection type.

• This interface provides a small set of properties that allow you to determine

(a) the number of items in the container,

(b) the thread safety of the container,

(c) the ability to copy the contents into a System.Array type.

• Formally, ICollection is defined as follows (note that ICollection extends IEnumerable):

public interface ICollection : IEnumerable

{

// IEnumerable member...

int Count { get; }

bool IsSynchronized { get; }

object SyncRoot { get; }

void CopyTo(Array array, int index);

}

IDictionary

• A dictionary is simply a collection that maintains a set of name/value pairs.

• For example, you could build a custom type that implements IDictionary such that you can store Car

types (the values) that may be retrieved by ID or pet name (e.g., names).

• Given this functionality, you can see that the IDictionary interface dpefines a Keys and Values

property as well as Add(), Remove(), and Contains() methods. The individual items may be obtained

by the type indexer.

public interface IDictionary :

ICollection, IEnumerable

{

bool IsFixedSize { get; }

bool IsReadOnly { get; }

object this[ object key ] { get; set; }

Page 25: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

25

ICollection Keys { get; }

ICollection Values { get; }

void Add(object key, object value);

void Clear();

bool Contains(object key);

IDictionaryEnumerator GetEnumerator();

void Remove(object key);

}

IDictionaryEnumerator

• If you were paying attention, you may have noted that IDictionary.GetEnumerator() returns an

instance of the IDictionaryEnumerator type

• IDictionaryEnumerator is simply a strongly typed enumerator, given that it extends IEnumerator by

adding the following functionality:

public interface IDictionaryEnumerator : IEnumerator

{

// IEnumerator methods...

DictionaryEntry Entry { get; }

object Key { get; }

object Value { get; }

}

In addition, you are also able to traverse the name/value pairs using the Key/Value properties.

IList

• The final key interface of System.Collections is IList, which provides the ability to insert, remove, and

index items into (or out of) a container:

public interface IList :

ICollection, IEnumerable

{

bool IsFixedSize { get; }

bool IsReadOnly { get; }

object this[ int index ] { get; set; }

int Add(object value);

void Clear();

bool Contains(object value);

int IndexOf(object value);

void Insert(int index, object value);

void Remove(object value);

void RemoveAt(int index);

}

The Class Types of System.Collections

• Interfaces by themselves are not very useful until they are implemented by a given class or structure

• Classes of System.Collections

Page 26: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

26

• In addition to these key types, System.Collections defines some minor players (at least in terms of

their day-to-day usefulness) such as BitArray, CaseInsensitiveComparer, and

CaseInsensitiveHashCodeProvider. Furthermore, this namespace also defines a small set of abstract

base classes (CollectionBase, ReadOnlyCollectionBase, and DictionaryBase) that can be used to build

strongly typed containers.

the ArrayList Type

• The ArrayList type is bound to be your most frequently used type in the System.Collections

namespace in that it allows you to dynamically resize the contents at your whim.

• To illustrate the basics of this type, ponder the following code, which leverages the ArrayList to

manipulate a set of Car objects:

static void Main(string[] args)

{

// Create ArrayList and fill with some initial values.

ArrayList carArList = new ArrayList();

carArList.AddRange(new Car[] { new Car("Fred", 90, 10),

new Car("Mary", 100, 50), new Car("MB", 190, 11)});

Console.WriteLine("Items in carArList: {0}", carArList.Count);

// Print out current values.

foreach(Car c in carArList)

Console.WriteLine("Car pet name: {0}", c.PetName);

// Insert a new item.

Console.WriteLine("\n->Inserting new Car.");

carArList.Insert(2, new Car("TheNewCar", 0, 12));

Console.WriteLine("Items in carArList: {0}", carArList.Count);

// Get object array from ArrayList and print again.

object[] arrayOfCars = carArList.ToArray();

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

{

Console.WriteLine("Car pet name: {0}",

Page 27: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

27

((Car)arrayOfCars[i]).PetName);

}

}

the Queue Type

• Queues are containers that ensure items are accessed using a first-in, first-out manner.

• When you are modeling a scenario in which items are handled on a first-come, first-served basis,

System.Collections.Queue is your type of choice. In addition to the functionality provided by the

supported interfaces, Queue defines the key members shown in Table 7-4.

public static void WashCar(Car c)

{

Console.WriteLine("Cleaning {0}", c.PetName);

}

Now, ponder the following code:

static void Main(string[] args)

{

...

// Make a Q with three items.

Queue carWashQ = new Queue();

carWashQ.Enqueue(new Car("FirstCar", 0, 1));

carWashQ.Enqueue(new Car("SecondCar", 0, 2));

carWashQ.Enqueue(new Car("ThirdCar", 0, 3));

// Peek at first car in Q.

Console.WriteLine("First in Q is {0}",

((Car)carWashQ.Peek()).PetName);

// Remove each item from Q.

WashCar((Car)carWashQ.Dequeue());

WashCar((Car)carWashQ.Dequeue());

WashCar((Car)carWashQ.Dequeue());

// Try to de-Q again?

try

{ WashCar((Car)carWashQ.Dequeue()); }

catch(Exception e)

{ Console.WriteLine("Error!! {0}", e.Message);}

}

Page 28: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

28

• Here, you insert three items into the Queue type via its Enqueue() method. The call to Peek() allows

you to view (but not remove) the first item currently in the Queue, which in this case is the car named

FirstCar.

• Finally, the call to Dequeue() removes the item from the line and sends it into the WashCar() helper

function for processing. Do note that if you attempt to remove items from an empty queue, a runtime

exception is thrown.

Working with the Stack Type

The System.Collections.Stack type represents a collection that maintains items using a last-in, first-out

manner. As you would expect, Stack defines a member named Push() and Pop() (to place items onto or

remove items from the stack). The following stack example makes use of the standard System.String:

static void Main(string[] args)

{

...

Stack stringStack = new Stack();

stringStack.Push("One");

stringStack.Push("Two");

stringStack.Push("Three");

// Now look at the top item, pop it, and look again.

Console.WriteLine("Top item is: {0}", stringStack.Peek());

Console.WriteLine("Popped off {0}", stringStack.Pop());

Console.WriteLine("Top item is: {0}", stringStack.Peek());

Console.WriteLine("Popped off {0}", stringStack.Pop());

Console.WriteLine("Top item is: {0}", stringStack.Peek());

Console.WriteLine("Popped off {0}", stringStack.Pop());

try

{

Console.WriteLine("Top item is: {0}", stringStack.Peek());

Console.WriteLine("Popped off {0}", stringStack.Pop());

}

catch(Exception e)

{ Console.WriteLine("Error!! {0}", e.Message);}

}

Here, you build a stack that contains three string types (named according to their order of insertion). As you

peek onto the stack, you will always see the item at the very top, and therefore the first call to Peek() reveals

the third string. After a series of Pop() and Peek() calls, the stack is eventuallyempty, at which time additional

Peek()/Pop() calls raise a system exception.

System.Collections.Specialized Namespace

In addition to the types defined within the System.Collections namespace, you should also be aware that the

.NET base class libraries provide the System.Collections.Specialized namespace, which defines another set

of types that are more (pardon the redundancy) specialized.

Page 29: Interfaces and Collections · 1 Interfaces and Collections Defining Interfaces in C# • An interface is a named collection of semantically related abstract members. • The specific

29