Dotnet unit 4


Published on

Published in: Education, Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Dotnet unit 4

  1. 1. Chapter 8Interfaces and Collections8.1 INTRODUCTION“An interface is nothing more than a named collection of semantically related abstract members.”The exact number of members defined by a given interface always depends on the exact behaviorthat a given class may support.8.2 DEFINING INTERFACES USING C#At a syntactic level, an interface is defined using the C# “interface” keyword. Unlike other .NETtypes, interfaces never specify a base class (not even System.Object) and contain members that donot take an access modifier (interface methods are implicitly public). To get the ball rolling, here isa custom interface definition: // This interface defines the behavior of ‘having points’. public interface IPointy { byte GetNumberOfPoints(); // Implicitly public and abstract. }.NET interfaces (C# or otherwise) are also able to define any number of properties. For example,you could modify the IPointy interface to use a read/write property: // The pointy behavior as a read / write property. public interface IPointy { // Remove ‘get’ or ‘set’ to build read/write only property. byte Points{ get; set;} }
  2. 2. Interfaces and Collections 2058.3 IMPLEMENTING AN INTERFACE USING C#When a C# class (or structure) chooses to extend its functionality by supporting a given interface,it does so using a comma-delimited list in the type definition. The direct base class must be listedfirst. // This class derives from System.Object and implements a singleinterface. public class SomeClass : ISomeInterface {...} // This class derives from a specific base class and implementsa single interface. public class AnotherClass : MyBaseClass, ISomeInterface {...} // This struct derives from System.ValueType and implements asingle interface. public struct SomeStruct : ISomeInterface {...}The shapes hierarchy Figure8.1: The shapes hierarchy // This interface defines the behavior of ‘having points’. public interface IPointy { byte GetNumberOfPoints(); // Implicitly public and abstract. } ___________________________________________________________________________ public abstract class Shape { protected string petName; public Shape() { petName = “NoName”; } public Shape(string s) { petName = s;} public virtual void Draw() { // Draw() is virtual and may be overridden. Console.WriteLine(“Shape.Draw()”);
  3. 3. 206 C# .Netprogramming } public string PetName { get { return petName;}set { petName = value;} } } ________________________________________________________________________________________ public class Circle : Shape { public Circle() { } public Circle(string name): base(name) { } } _______________________________________________________________________________________ // A given class may implement as many interfaces as necessary, butmay have exactly 1 base class. public class Hexagon : Shape, IPointy { public Hexagon(){ } public Hexagon(string name) : base(name){ } public override void Draw() { // Shape class defined the PetName property. Console.WriteLine(“Drawing {0} the Hexagon”, PetName); } // IPointy Implementation. public byte GetNumberOfPoints() { return 6; } } ________________________________________________________________________ public class Triangle : Shape, IPointy { public Triangle() { } public Triangle(string name) : base(name) { } public override void Draw() { Console.WriteLine(“Drawing {0} the Triangle”, PetName); } public byte GetNumberOfPoints() { // IPointy Implementation. return 3; } } _________________________________________________________________________ Each class now returns the number of points to the outside world when asked todo so.
  4. 4. Interfaces and Collections 2078.3.1 Invoking Interface Members at the object LevelThe way to interact with functionality supplied by a given interface is to invoke the methods directlyfrom the object level. For example: Hexagon hex = new Hexagon(); Console.WriteLine(“Points: {0}”, hex.GetNumberOfPoints());This approach works fine in this case, given that you are well aware that the Hexagon type hasimplemented the interface. There is number of situation you will be required to obtain a valid interface reference directlybefore you are able to activate said functionality.8.3.2 Obtaining Interface References: Explicit CastingAssume you have created an instance of the Hexagon class, and wish to dynamically see if itsupports the pointy behavior. One approach is to make use of an explicit cast in order to obtain aninterface reference: Hexagon hex = new Hexagon(“Bill”); IPointy itfPt = (IPointy)hex; Console.WriteLine(itfPt.GetNumberOfPoints());If you do not require a stand-alone interface reference, you could shorten the previous code into asingle step as follows: Hexagon hex = new Hexagon(“Bill”); Console.WriteLine( ( (IPointy)hex ).GetNumberOfPoints() );In each of these cases, you are explicitly asking the Hexagon instance for access to the IPointyinterface. If the object does support this interface, you are then able to exercise the behavioraccordingly. When you attempt to access an interface not supported by a given class using a direct cast, theruntime throws an InvalidCastException. To safely recover from this possibility, simply catch theexception: Circle c = new Circle(“Lisa”); IPointy itfPt; try{ itfPt = (IPointy)c; Console.WriteLine( itfPt.GetNumberOfPoints() ); } catch(InvalidCastException e) { Console.WriteLine(“OOPS! Not an Ipointy type ...”); }
  5. 5. 208 C# .Netprogramming8.3.3 Obtaining Interface References: The “as” KeywordThe second way you can test for interface support is to make use of the “as” keyword For example: Hexagon hex2 = new Hexagon(“Peter”); IPointy itfPt2; itfPt2 = hex2 as IPointy; if( itfPt2 != null ) Console.WriteLine(itfPt2.GetNumberOfPoints()); else Console.WriteLine(“OOPS! Not pointy...”);As you can see, the “as” syntax sets the interface variable to null if a given interface is not supportedby the object (notice that you check your IPointy reference for null before continuing) rather thanthrowing an exception.8.3.4 Obtaining Interface References: The “is” KeywordFinally, you may also obtain an interface from an object using the “is” keyword. If the object inquestion is not IPointy compatible, the condition fails: Triangle t = new Triangle(); if( t is IPointy ) Console.WriteLine( t. GetNumberOfPoints() ); else Console.WriteLine(“OOPS! Not pointy...”);Exercising the Shapes HierarchyCreate an array of generic Shape references, each of which has been assigned to a given subclass?You may make use of any of the previous techniques to discover at runtime which items in the arraysupport this behavior: // Let’s discover which shapes are pointy at runtime... Shape[] s = { new Hexagon(), new Circle(), new Triangle(“Joe”),new Circle(“JoJo”)} ; for(int i = 0; i < s.Length; i++) { // Shape base class defines an abstract Draw() member, so all shapes know howto draw themselves. s[i].Draw(); // Who’s pointy? if(s[i]is IPointy)
  6. 6. Interfaces and Collections 209 Console.WriteLine(“-> Points: {0} “, ((IPointy)s[i]).GetNumberOfPoints()); else Console.WriteLine(“-> {0}’s not pointy!”, s[i].PetName); }The output follows in Figure. Figure 8.2: output8.4 INTERFACES AS PARAMETERSGiven that interfaces are strongly typed entities, you may construct methods that take interfaces asparameters as well as method return values. To illustrate, assume you have defined another interfacenamed IDraw3D as follows: The Updated Shape Hierarchy Figure8.3: Updated shape hierarch // The 3D drawing behavior. public interface IDraw3D { void Draw3D(); } Next, assume that two of your three shapes (Circle and Hexagon) have beenconfigured to support this new behavior:
  7. 7. 210 C# .Netprogramming // Circle supports IDraw3D. public class Circle : Shape, IDraw3D { ... public void Draw3D() { Console.WriteLine(“Drawing Circle in 3D!”); } } // If your types support multiple interfaces, simply tack them to the end ofthe class definition. public class Hexagon : Shape, IPointy, IDraw3D { ... public void Draw3D() { Console.WriteLine(“Drawing Hexagon in 3D!”); } }If you now define a method taking an IDraw3D interface as a parameter, you are able to effectivelysend in any object supporting IDraw3D. Consider the following: public class ShapesApp { // I’ll draw anyone supporting IDraw3D! public static void DrawThisShapeIn3D( IDraw3D itf3d ) { Console.WriteLine(“-> Drawing IDraw3D compatible type”); itf3d.Draw3D(); } public static int Main(string[] args) { 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) DrawThisShapeIn3D( (IDraw3D)s[i] ); } return 0; } }8.5 UNDERSTANDING EXPLICIT INTERFACE IMPLEMENTATIONIn our previous definition of IDraw3D, you were forced to name your method Draw3D() in order toavoid clashing with the abstract Draw() method defined in the Shapes base class:
  8. 8. Interfaces and Collections 211 // The 3D drawing behavior. public interface IDraw3D { void Draw3D(); }While there is nothing wrong with this interface definition, a more natural method name wouldsimply be Draw(): // The 3D drawing behavior. public interface IDraw3D { void Draw (); }If you were to create a new class that derives from Shape and implements IDraw3D, you would bein for some problematic behavior. Before seeing the problem firsthand, assume you have definedthe following new class named Line: // Problems... public class Line : Shape, IDraw3D // Both define a Draw() method! { public override void Draw() { Console.WriteLine(“Drawing a line...”); } }The Line class compiles without a hitch. But, consider the following object user code: // Calls Line.Draw() Line myLine = new Line(); myLine.Draw(); // Also calls Line.Draw(). IDraw3D itfDraw3d= (IDraw3D) myLine; itfDraw3d.Draw();Given what you already know about the Shapes base class and IDraw3D interface, it looks as ifyou have acquired two abstract methods named Draw(). However, as the Line class offers a concreteimplementation, the compiler is happy to call the same implementation from an interface or objectreference. The explicit interface implementation, you are able to ensure that the object user can onlyaccess methods defined by a given interface using the correct interface reference, as well ascircumvent possible name clashes. To illustrate, here is the updated Line class: public class Line : Shape, IDraw3D { // You can only call this method using an IDraw3D interface reference.
  9. 9. 212 C# .Netprogramming void IDraw3D.Draw() { Console.WriteLine(“Drawing a 3D line...”); } // You can only call this using a Line (or base class) reference. public override void Draw() { Console.WriteLine(“Drawing a line...”); } } There are a few odds and ends to be aware of when using explicit interface implementation.First and foremost, you cannot make use of an access modifier when using this technique. Forexample, the following is illegal syntax: // Nope! Illegal. public class Line : Shape, IDraw3D { public void IDraw3D.Draw() { // <= Error! Console.WriteLine(“Drawing a 3D line...”); } ... }The whole reason to use explicit interface method implementation is to ensure that a given interfacemethod is bound at the interface level. If you were to add the “public” keyword, this would suggestthat the method is a member of the public sector of the class. Given this design, the caller is unableto invoke IDraw3D.Draw() from an object level: // This triggers the overridden Shape.Draw() method. Line l = new Line(); l.Draw();Explicit interface implementation can be very helpful whenever you are implementing a number ofinterfaces that happen to contain identical methods. For example, assume you wish to create a classthat implements all the following interfaces: // Three interfaces each defining identical methods. public interface IDraw { void Draw(); } public interface IDraw3D { void Draw(); } public interface IDrawToPrinter { void Draw(); }
  10. 10. Interfaces and Collections 213If you wish to build a shape (using interface-based techniques) that supports basic rendering (IDraw),3D rendering (IDraw3D), as well as printing services (IDrawToPrinter), the only way to provideunique behaviors for each method is to use explicit interface implementation: // Not deriving from Shape, but still injecting a name clash. public class SuperImage : IDraw, IDrawToPrinter, IDraw3D { void IDraw.Draw() { // Basic drawing logic. } void IDrawToPrinter.Draw() { // Printer logic. } void IDraw3D.Draw() { // 3D support. } }8.6 INTERFACES AS POLYMORPHIC AGENTSAs you already know, an abstract base class (containing abstract members) allows us to define aspecific behavior that is common across all members in the same class hierarchy. To really understandthe usefulness of interfaces, assume that the GetNumberOfPoints() method was not defined byIPointy, but rather as an abstract member of the Shape class. If this were the case, Hexagon andTriangle would still be able to return the correct result. At the same time, Circle would also beobligated to contend with the GetNumberOfPoints() method as well. In this case, however, it mightseem a bit inelegant to simply return 0. The problem with defining GetNumberOfPoints() in the Shapes base class is that all derivedtypes must contend with this member, regardless of the semantics. Thus, the first key point aboutinterfaces is the fact that you can select which members in the hierarchy support custom behaviors.Also understand that the same interface can be implemented by numerous types, even if they arenot within the same class hierarchy in the first place. This can yield some very powerful programmingconstructs. For example, assume that you have developed a brand new class hierarchy modeling kitchenutensils and another modeling gardening equipment. Although each hierarchy of types is completelyunrelated from a classical inheritance point of view, you can link them together using the commonbehavior supplied by the IPointy interface: // This array can only contain types which implement the IPointy interface. IPointy myPointyObjects[] = {new Hexagon(), new Knife(),new Triangle(), newFork(), new PitchFork()};At this point, you are able to iterate through the array and treat each object as an IPointy-compatibleobject, regardless of the overall diversity of the class hierarchies. In fact, the IDisposable interfacemay be implemented by numerous .NET types, regardless of which assembly they reside in. Atruntime, you are able to determine if the object supports this behavior (using an explicit cast or the“as”/”is” keywords) and call the Dispose() method.
  11. 11. 214 C# .Netprogramming8.7 BUILDING INTERFACE HIERARCHIES:Just as a class can serve as a base class to other classes (base classes to yet another class), it ispossible to build derived relationships among interfaces. As you might expect, the topmost interfacedefines a general behavior, while the most derived interface defines more specific behaviors. Toillustrate, consider the following interface hierarchy: // The base interface. interface IDraw { void Draw();} interface IDraw2 : IDraw { void DrawToPrinter(); } interface IDraw3 : IDraw2 { void DrawToMetaFile(); }Understand, of course, that you can name your derived interfaces anything you choose. Here Idemonstrate how to make use of a common COM-centric naming convention, which is to suffix anumerical qualifier to the derived type. The relationships between these custom interfaces can beseen in Figure . Now, if a class wished to support each behavior expressed in this interface hierarchy, it wouldderive from the nth-most interface (IDraw3 in this case). Any methods defined by the base interface(s)are automatically carried into the definition. For example: Figure 8.4: interface inheritance // This class supports IDraw, IDraw2 and IDraw3. public class SuperImage : IDraw3 { // in this case, as there is no name clash. However, by doing so, we will forcethe user to obtain the interface first. void IDraw.Draw() { // Basic drawing logic } void IDraw2.DrawToPrinter() { // Draw to printer. } void IDraw3.DrawToMetaFile() { // Draw to metafile. } }
  12. 12. Interfaces and Collections 215Here is some sample usage: // Exercise the interfaces. public class TheApp { public static int Main(string[] args) { SuperImage si = new SuperImage(); IDraw itfDraw = (IDraw)si; itfDraw.Draw(); if(itfDraw is IDraw3) // Now get IDraw3. { IDraw3 itfDraw3 = (IDraw3)itfDraw; itfDraw3.DrawToMetaFile(); itfDraw3.DrawToPrinter(); } return 0; } }8.8 INTERFACES WITH MULTIPLE BASE INTERFACESAs you build interface hierarchies, be aware that it is completely permissible to create an interfacethat derives from multiple base interfaces. Recall, of course, that it is not permissible to build aclass that derives from multiple base classes. For example, assume you are building a new set ofinterfaces that model automobile behaviors: interface IBasicCar { void Drive(); } interface IUnderwaterCar { void Dive(); } // Here we have an interface with TWO base interfaces. interface IJamesBondCar : IBasicCar, IUnderwaterCar{ void TurboBoost(); }If you were to build a class that implements IJamesBondCar, you would now be responsible forimplementing TurboBoost(), Dive(), and Drive(): public class JBCar : IJamesBondCar { public JBCar(){ } // Again, we are not required to use explicit interface implementation, aswe have no name clashes. void IBasicCar.Drive(){ Console.WriteLine(“Speeding up...”);} void IUnderwaterCar.Dive(){ Console.WriteLine(“Submerging...”);} void IJamesBondCar.TurboBoost(){ Console.WriteLine(“Blast off!”);} }
  13. 13. 216 C# .NetprogrammingThis specialized automobile can now be manipulated as you would expect: JBCar j = new JBCar(); if( j is IJamesBondCar ) { ( ( IJamesBondCar )j ).Drive(); ( ( IJamesBondCar )j ).TurboBoost(); ( ( IJamesBondCar )j ).Dive(); }8.9 IMPLEMENTING INTERFACES USING VS .NETAlthough interface-based programming is a very powerful programming technique, one drawbackis the very simple fact that you are required to do a lot of manual typing. Given that interfaces area named set of abstract members, you will be required to type in the stub code (and implementation)for each interface method on each class that supports the behavior. As you would expect, VS .NETdoes support an integrated tool that helps make this task easy. To illustrate, assume you have aninterface defining the following four methods: public interface IAmAnInterface { void MethodA(); void MethodC(); void MethodD(); void MethodE(); }And also assume you have a class that supports IAmAnInterface: public class SomeClass : IAmAnInterface {}At this point, you are free to build stub code for each abstract method by hand. However, if youwish to force VS .NET, switch to Class View and find a class that already specifies support for theinterface you wish to implement. Once you do, expand the Bases and Interfaces node, right-clickthe interface icon, and select Add | Implement Interface Figure. Figure 8.5 : interface implementation
  14. 14. Interfaces and Collections 217 namespace IFaceHierarchy { public class MiniVan : ICar { public MiniVan(){ } #region ICar Members public void Drive() { new Exception(“not implemented.”); } #endregion } }Once you do, you will see that VS .NET has built automatic stub code wrapped in a #region/#endregion pair. when you insert a new class definition into your VS .NET projects using ClassView, you are able to specify the set of implemented interfaces at the time of creation. When you doso, you will still need to run the Implement Interface Wizard as shown previously.8.10 UNDERSTANDING THE ICONVERTIBLE INTERFACE:The IConvertible type, which allows you to dynamically convert between data types using interface-based programming techniques. Using this interface, you are able to cast between types on the flyusing language-agnostic terms. Majority of the intrinsic data types of C# (bool, int, double, etc.) are simply aliases to true-bluestructures in the System namespace. Like any .NET type, each of these structures may define anynumber of methods and may optionally implement any number of predefined interfaces. For example,if you were to view the formal C# definition of System.Boolean (using wincv.exe), you would findthis type (as well as all intrinsic data type structures) implements the IComparable and IConvertibleinterfaces: public struct Boolean : IComparable , IConvertible { public static readonly string FalseString; public static readonly string TrueString; public virtual int CompareTo(object obj); public virtual bool Equals(object obj); public virtual int GetHashCode(); public Type GetType(); public virtual TypeCode GetTypeCode(); public static bool Parse(string value); public virtual string ToString();
  15. 15. 218 C# .Netprogramming public virtual string ToString(IFormatProvider provider); } public interface IConvertible { TypeCode GetTypeCode(); bool ToBoolean(IFormatProvider provider); byte ToByte(IFormatProvider provider); char ToChar(IFormatProvider provider); DateTime ToDateTime(IFormatProvider provider); Decimal ToDecimal(IFormatProvider provider); double ToDouble(IFormatProvider provider); short ToInt16(IFormatProvider provider); int ToInt32(IFormatProvider provider); long ToInt64(IFormatProvider provider); SByte ToSByte(IFormatProvider provider); float ToSingle(IFormatProvider provider); string ToString(IFormatProvider provider); object ToType(Type conversionType, IFormatProvider provider); UInt16 ToUInt16(IFormatProvider provider); UInt32 ToUInt32(IFormatProvider provider); UInt64 ToUInt64(IFormatProvider provider); } bool myBool = true; IConvertible itfConv = (IConvertible)myBool;8.10.1 IConvertible.ToXXXX() MembersI Convertible interface defines a number of methods of the form ToXXXX(), which as you canmost likely tell, provide a way to convert from one type into another. As you may also be able totell, it may not always be possible to convert between data types. For example, although it is naturalto predict converting from an Int32 into a Double, it really makes no sense to convert from aBoolean into a DateTime. Given the fact that when a type implements an interface, it must contend with all methods (evenif they are not applicable), the system types will simply throw an InvalidCastException if theconversion is semantically not well formed: // Obtain the IConvertible interface. bool myBool = true; IConvertible itfConv = (IConvertible)myBool; try{ itfConv.ToSingle(...);
  16. 16. Interfaces and Collections 219 } catch(InvalidCastException e) { Console.WriteLine(e); }8.10.2 A Brief Word Regarding IFormatProviderNotice that all of the ToXXXX() methods take a parameter of type IFormatProvider. Objects thatimplement this interface are able to format their contents based on culture-specific information (forexample, returning a floating point number that is formatted in various currencies). Here is theformal definition: public interface IFormatProvider { object GetFormat(Type formatType); }If you were to build a custom type that should be formatted using various locals, implementingIFormatProvider would be a must. However, if you are simply attempting to call members of thebase class libraries that require an IFormatProvider-compatible object, feel free to leverage theSystem.Globalization.CultureInfo type as follows: IConvertible itfConvert = (IConvertible)theInt; byte theByte = itfConvert.ToByte(CultureInfo.CurrentCulture); Console.WriteLine(“Type code int converted to byte is: {0}”,theByte.GetTypeCode()); Console.WriteLine(“Value of converted int: {0}”, theByte);8.10.3 IConvertible.GetTypeCode()In addition to the ToXXXX() members, IConvertible defines a member named GetTypeCode().This method, which is available to any class or structure implementing IConvertible, allows you toprogrammatically discover a value that represents the type code of the type, which is representedby the following enumeration: public enum TypeCode { Boolean, Byte, Char, DateTime,DBNull, Decimal, Double, Empty, Int16, Int32, Int64, Object,SByte, Single, String, UInt16,UInt32,UInt64 }To be sure, IConvertible.GetTypeCode() is not a method you will need to call all that often in yourday-today programming endeavors.
  17. 17. 220 C# .Netprogramming8.11 BUILDING A CUSTOM ENUMERATOR (IENUMERABLE AND ENUMERATOR)To illustrate the process of implementing existing .NET interfaces, let’s first examine the role ofIEnumerable and IEnumerator. Assume you have developed a class named Garage that contains aset 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); carArray[3] = new Car(“Fred”, 30); } }Ideally, it would be convenient to iterate over the Garage object’s subitems using the C# foreachconstruct: // 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 compiler informs you that the Garage class does not implement a method namedGetEnumerator(). This method is formalized by the IEnumerable interface, which is found lurkingwithin the System.Collections namespace. Objects that support this behavior advertise that theyare able to expose contained subitems to the caller: // This interface informs the caller that the object’s subitems can beenumerated. public interface IEnumerable { IEnumerator GetEnumerator(); }
  18. 18. Interfaces and Collections 221As you can see, the GetEnumerator() method returns a reference to yet another interface namedSystem.Collections.IEnumerator. This interface provides the infrastructure to allow the caller totraverse the internal objects contained by the IEnumerable-compatible container: // This interface allows the caller to obtain a container’s sub items. 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, it will take time to implementeach method manually. As the System.Array type 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# foreachconstruct. Furthermore, given that the GetEnumerator() method has been defined publicly, theobject user could also interact with the IEnumerator type: // Manually work with IEnumerator. IEnumerator i = carLot.GetEnumerator();
  19. 19. 222 C# .Netprogramming 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 useof explicit interface implementation: public IEnumerator IEnumerable.GetEnumerator() { // Return the array object’s IEnumerator. return carArray.GetEnumerator(); }8.12 UNDERSTANDING C# ITERATOR METHODSUnder .NET 1.x, if you wished to have your custom collections (such as Garage) support foreachlike enumeration, implementing the IEnumerable interface (and possibly the IEnumerator interface)was mandatory. However, offers an alternative way to build types that work with the foreach loopvia iterators. Simply put, an iterator is a member that specifies how a container’s internal items should bereturned 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 implementany 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 foreachlogic and returns each Car to the caller using the new yield return syntax. The yield keyword is usedto specify the value (or values) to be returned to the caller’s foreach construct. When the yieldreturn statement is reached, the current location is stored, and execution is restarted from thislocation the next time the iterator is called.
  20. 20. Interfaces and Collections 223 When the C# compiler encounters an iterator method, it will dynamically generate a nestedclass within the scope of the defining type (Garage in this case). The autogenerated class implementsthe GetEnumerator(), MoveNext() and Current members on your behalf (oddly, the Reset() methodis not, and you will receive a runtime exception if you attempt to call it). If you were to load thecurrent application into ildasm.exe, you would find that the Garage’s implementation ofGetEnumerator() is making use of this compiler-generated type (which happens to be named<GetEnumerator>d__0 in this example) 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::GetEnumeratorHowever, if you are building a more exotic custom container (such as a binary tree) where you needto manually implement the IEnumerator and IEnumerable interfaces, the C# iterator syntax can bea massive time-saver. In any case, the caller’s code is identical when interacting with a type’siterator 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(); }8.13 BUILDING CLONEABLE OBJECTS ( ICLONEABLE)System.Object defines a member named MemberwiseClone(). This method is used to obtain ashallow 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,assumeyou have a class named Point:
  21. 21. 224 C# .Netprogramming // 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 ); } }The following assignment operation results in two references to the same Point object on the heap;modifications using either reference affect the same object on the heap: 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); }The ability to return an identical copy of itself to the caller, you may implement the standardICloneable interface. This type defines a single method named Clone(): public interface ICloneable { object Clone(); }The basic functionality is Copy the values of your member variables into a new object instance, andreturn it to the user. To illustrate, consider 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.
  22. 22. Interfaces and Collections 225 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 followingcode: static void Main(string[] args) { // Notice Clone() returns a generic object 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); }The Point type does not contain reference type variables, you could simplify the implementation ofthe Clone() method as follows: public object Clone() { // Copy each field of the Point member by member. return this.MemberwiseClone(); }If the Point did contain any reference type member variables, MemberwiseClone() will copy thereferences to those objects. If you wish to support a true deep copy, you will need to create a newinstance of any reference type variables during the cloning process. Let’s see an example.A More Elaborate Cloning ExampleNow assume the Point class contains a reference type member variable of type PointDescription. This class maintains a point’s friendly name as well as an identification number expressed as aSystem.Guid. Here is the implementation: // This class describes a point. public class PointDescription { // Exposed publicly for simplicity. public string petName;
  23. 23. 226 C# .Netprogramming public Guid pointID; public PointDescription() { this.petName = “No-name”; pointID = Guid.NewGuid(); } } public class Point : ICloneable { public int x, y; 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); } }Notice that you did not yet update your Clone() method. Therefore, when the object user asks for aclone using the current implementation, a shallow (member-by-member) copy is achieved. Toillustrate, assume you have updated Main() as follows: 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:”);
  24. 24. Interfaces and Collections 227 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); }Figure shows the output Figure 8.6: outputIn order for your Clone() method to make a complete deep copy of the internal reference types, youneed to configure the object returned by MemberwiseClone() to account for the current point’sname. 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; return newPoint; }
  25. 25. 228 C# .NetprogrammingIf you rerun the application once again as shown in Figure , you see that the Point returned fromClone() does copy its internal reference type member variables (note the pet name is now uniquefor both p3 and p4).8.14 BUILDING COMPARABLE OBJECTS (ICOMPARABLE)The System.IComparable interface specifies a behavior that allows an object to be sorted based onsome specified key. Here is the formal definition: // This interface allows an object to specify its relationship between otherlike objects. public interface IComparable { int CompareTo(object o); }Let’s assume you have updated the Car class to maintain an internal ID number (represented by asimple integer named carID) that can be set via a constructor parameter and manipulated using anew property named ID. Here are the relevant updates to the Car type: 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; } ... }Object users might create an array of Car types as follows: static void Main(string[] args) {
  26. 26. Interfaces and Collections 229 // 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); }The System.Array class defines a static method named Sort(). When you invoke this method on anarray of intrinsic types (int, short, string, etc.), you are able to sort the items in the array in numerical/alphabetic order as these intrinsic data types implement IComparable. To send an array of Car types into the Sort() method as follows Array.Sort(myAutos);If you run this test, you would find that an ArgumentException exception is thrown by the runtime,with the following message: “At least one object must implement IComparable.” When you buildcustom types, you can implement IComparable to allow arrays of your types to be sorted. // 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; } }As you can see, the logic behind CompareTo() is to test the incoming type against the currentinstance based on a specific point of data. The return value of CompareTo() is used to discover ifthis type is less than, greater than, or equal to the object it is being compared with (see Table).
  27. 27. 230 C# .Netprogramming Table 8.1 CompareTo() Return Value Meaning in Life Any number less than zero This instance comes before the specified object in the sort order. Zero This instance is equal to the specified object. Any number greater than zero This instance comes after the specified object in the sort order. Now that your Car type understands how to compare itself to like objects, you can write thefollowing user code: 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); Array.Sort(myAutos); Console.WriteLine(“Here is the ordered set of cars:”); foreach(Car c in myAutos) Console.WriteLine(“{0} {1}”, c.ID, c.PetName); Console.ReadLine(); } Figure8.7 : Illustrates a test run.
  28. 28. Interfaces and Collections 2318.15 SPECIFYING MULTIPLE SORT ORDERS (ICOMPARER)In this version of the Car type, you made use of the car’s ID to function as the baseline of the sortorder. if you wanted to build a Car that could be sorted by ID as well as by pet name then you needto make friends with another standard interface named IComparer, defined within theSystem.Collections namespace as follows: // A generic way to compare two objects. interface IComparer { int Compare(object o1, object o2);} Unlike the IComparable interface, IComparer is typically not implemented on the type you aretrying to sort (i.e., the Car). Rather, you implement this interface on any number of helper classes,one for each sort order (pet name, car ID, etc.). // 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 overloadedSort() methods, one that just happens to take an object implementing IComparer: 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); ... }
  29. 29. 232 C# .Netprogramming8.16 EXPLORING THE SYSTEM. COLLECTIONS NAMESPACEFirst of all, System.Collections defines a number of standard interfaces. Most of the classes definedwithin the System.Collections namespace implement these interfaces to provide access to theircontents. Table gives a breakdown of the core collectioncentric interfaces. Table 8.2 System.Collections Interface Meaning in Life ICollection Defines generic characteristics (e.g., count and thread safety) for a collection type. 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. 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 (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”. Many of these interfaces are related by an interface hierarchy, while others are stand-aloneentities. Figure illustrates the relationship between each type. Figure 8.8: Interface hierarchy
  30. 30. Interfaces and Collections 2338.16.1 Role of ICollectionThe ICollection interface is the most primitive interface of the System.Collections namespace inthat it defines a behavior supported by a collection type. In a nutshell, this interface provides asmall set of properties that allow you to determine (a) the number of items in the container, (b) thethread safety of the container, as well as (c) the ability to copy the contents into a System.Arraytype. 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); }8.16.2 Role of IDictionaryAs you may already be aware, a dictionary is simply a collection that maintains a set of name/valuepairs. For example, you could build a custom type that implements IDictionary such that you canstore Car types (the values) that may be retrieved by ID or pet name (e.g., names). Given thisfunctionality, you can see that the IDictionary interface defines a Keys and Values property as wellas Add(), Remove(), and Contains() methods. The individual items may be obtained by the typeindexer. Here is the formal definition: public interface IDictionary :ICollection, IEnumerable { bool IsFixedSize { get; } bool IsReadOnly { get; } object this[ object key ] { get; set; } ICollection Keys { get; } ICollection Values { get; } void Add(object key, object value); void Clear(); bool Contains(object key); IDictionaryEnumerator GetEnumerator(); void Remove(object key); }
  31. 31. 234 C# .Netprogramming8.16.3 Role of IDictionaryEnumeratorIf you were paying attention, you may have noted that IDictionary.GetEnumerator() returns aninstance of the IDictionaryEnumerator type. IDictionaryEnumerator is simply a strongly typedenumerator, 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; } }Notice how IDictionaryEnumerator allows you to enumerate over items in the dictionary via thegeneric Entry property, which returns a System.Collections.DictionaryEntry class type. In addition,you are also able to traverse the name/value pairs using the Key/Value properties.8.16.4 Role of IListThe 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); }8.17 CLASS TYPES OF SYSTEM.COLLECTIONSAs I hope you understand by this point in the chapter, interfaces by themselves are not very usefuluntil they are implemented by a given class or structure. Table provides a rundown of the coreclasses in the System.Collections namespace and the key interfaces they support.
  32. 32. Interfaces and Collections 235 Table 8.3 System. Meaning in Life Key Implemented Interfaces Collections Class ArrayList Represents a dynamically sized IList, ICollection, IEnumerable, and ICloneable array of objects Hashtable Represents a collection of objects IDictionary, ICollection, IEnumerable, and identified by a numerical key. ICloneable Custom types stored in a Hashtable shouldalways override System. Object.GetHashCode(). Queue Represents a standard first-in, FIFO ICollection, ICloneable, and IEnumerable queue SortedList Like a dictionary; however, the IDictionary, ICollection, IEnumerable, and elements can also be accessed by ICloneable ordinal position Stack A last-in, first-out (LIFO) queue ICollection, ICloneable, and IEnumerable providing push and pop (and peek) functionality.8.17.1 Working with the ArrayList TypeThe ArrayList type it allows you to dynamically resize the contents at your notion. To illustrate thebasics of this type, consider the following code, which shows the ArrayList to manipulate a set ofCar 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); foreach(Car c in carArList) Console.WriteLine(“Car pet name: {0}”, c.PetName); Console.WriteLine(“n->Inserting new Car.”); carArList.Insert(2, new Car(“TheNewCar”, 0, 12)); Console.WriteLine(“Items in carArList: {0}”, carArList.Count); object[] arrayOfCars = carArList.ToArray();
  33. 33. 236 C# .Netprogramming for(int i = 0; i < arrayOfCars.Length; i++) { Console.WriteLine(“Car pet name: {0}”,((Car)arrayOfCars[i]).PetName); } }8.17.2 Working with the Queue TypeQueues are containers that ensure items are accessed using a first-in, first-out manner. When youare 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 thesupported interfaces, Queue defines the key members shown in Table. Table 8.4 Member of System. Meaning in Life Collection.Queue Dequeue() Removes and returns the object at the beginning of the Queue Enqueue() Adds an object to the end of the Queue Peek() Returns the object at the beginning of the Queue without removing it To illustrate these methods, we will leverage our automobile theme once again and build aQueue object that simulates a line of cars waiting to enter a car wash. First, assume the followingstatic helper method: public static void WashCar(Car c) { Console.WriteLine(“Cleaning {0}”, c.PetName); } Now, consider 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());
  34. 34. Interfaces and Collections 237 WashCar((Car)carWashQ.Dequeue()); // Try to de-Q again? try { WashCar((Car)carWashQ.Dequeue()); } catch(Exception e) { Console.WriteLine(“Error!! {0}”, e.Message);} }Here, you insert three items into the Queue type via its Enqueue() method. The call to Peek() allowsyou to view (but not remove) the first item currently in the Queue, which in this case is the carnamed FirstCar. Finally, the call to Dequeue() removes the item from the line and sends it into theWashCar() helper function for processing. Do note that if you attempt to remove items from anempty queue, a runtime exception is thrown.8.17.3 Working with the Stack TypeThe 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 placeitems onto or remove items from the stack). The following stack example makes use of the standardSystem.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);} }
  35. 35. 238 C# .NetprogrammingHere, you build a stack that contains three string types (named according to their order ofinsertion). As you peek onto the stack, you will always see the item at the very top, and therefore the firstcall 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.8.18 SYSTEM.COLLECTIONS.SPECIALIZED NAMESPACEIn addition to the types defined within the System.Collections namespace, you should also beaware 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. For example,the StringDictionary and ListDictionary types each provide a stylized implementation of theIDictionary interface. Table documents the key class types. Table 8.5 Member of System.Collections.Specialized Meaning in Life CollectionsUtil Creates collections that ignore the case in strings. HybridDictionary Implements IDictionary by using a ListDictionary while the collection is small, and then switching to a Hashtable when the collection gets large. ListDictionary Implements IDictionary using a singly linked list. Recommended for collections that typically contain ten items or fewer. NameValueCollection Represents a sorted collection of associated String keys and String values that can be accessed either with the key or with the index. StringCollection Represents a collection of strings. StringDictionary Implements a hashtable with the key strongly typed to be a string rather than an object. StringEnumerator Supports a simple iteration over a StringCollection. SUMMARYThe .NET framework defines a collection namespace of system .which contains many classes andinterfaces, provides to define various collection of objects every programmer usage this class andinterfaces to develop their applications .user can define their own collection too. QUESTIONS 1. What is an interface? with a program demonstrate the implicit and explicit access of interface. 2. Write the three methods of ICompare interface class.
  36. 36. Interfaces and Collections 239 3. Why interfaces are used in C# programming? With example explain any four interfaces of System.Collection. 4. Write a C# program which contains following: (a) an interface called dimension with the methods length() and width() which returns length and width in centimeters. (b) another interface called metric dimension with the method lengthinches() and widthinches() , which returns length and width in inches. (c) a class box that implements both the above said interfaces. This class has two data member’s lengthinches and widthinches. Define appropriate constructor for the class box write a main program of create an instance of box and to display the box length and width in inches and centimeters by ionvoking the appropriate methods of two interface. 5. What are the major difference and similarities between a class and an interface? 6. All the member of interface are implicitly abstract .what is the implication of this ? 7. We can combine two or more interface together. Discuss. 8. Describe various forms of implementing interfaces. 9. What is explicit interface implementation? When is it used? 10. When an interface inherit another interface, we call it an extending an interface .But when a class inherits an interface, we call it as implementing an interface. Why?