SlideShare a Scribd company logo
1 of 36
Chapter                      8
Interfaces and Collections

8.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 behavior
that 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 .NET
types, interfaces never specify a base class (not even System.Object) and contain members that do
not take an access modifier (interface methods are implicitly public). To get the ball rolling, here is
a 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;}
      }
Interfaces and Collections                                                                      205

8.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 listed
first.
         // This class derives from System.Object and implements a single
interface.
      public class SomeClass : ISomeInterface
      {...}
      // This class derives from a specific base class and implements
a single interface.
      public class AnotherClass : MyBaseClass, ISomeInterface
      {...}
      // This struct derives from System.ValueType and implements a
single 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()”);
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, but
may 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 to
do so.
Interfaces and Collections                                                                        207

8.3.1 Invoking Interface Members at the object Level
The way to interact with functionality supplied by a given interface is to invoke the methods directly
from 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 has
implemented the interface.
   There is number of situation you will be required to obtain a valid interface reference directly
before you are able to activate said functionality.

8.3.2 Obtaining Interface References: Explicit Casting
Assume you have created an instance of the Hexagon class, and wish to dynamically see if it
supports the pointy behavior. One approach is to make use of an explicit cast in order to obtain an
interface 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 a
single 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 IPointy
interface. If the object does support this interface, you are then able to exercise the behavior
accordingly.
    When you attempt to access an interface not supported by a given class using a direct cast, the
runtime throws an InvalidCastException. To safely recover from this possibility, simply catch the
exception:
         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 ...”);
         }
208                                                                                  C# .Netprogramming


8.3.3 Obtaining Interface References: The “as” Keyword
The 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 supported
by the object (notice that you check your IPointy reference for null before continuing) rather than
throwing an exception.

8.3.4 Obtaining Interface References: The “is” Keyword
Finally, you may also obtain an interface from an object using the “is” keyword. If the object in
question 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 Hierarchy
Create 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 array
support 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 how
to draw themselves.
           s[i].Draw();
           // Who’s pointy?
           if(s[i]is IPointy)
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: output



8.4 INTERFACES AS PARAMETERS
Given that interfaces are strongly typed entities, you may construct methods that take interfaces as
parameters as well as method return values. To illustrate, assume you have defined another interface
named 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 been
configured to support this new behavior:
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 of
the 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 effectively
send 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 IMPLEMENTATION
In our previous definition of IDraw3D, you were forced to name your method Draw3D() in order to
avoid clashing with the abstract Draw() method defined in the Shapes base class:
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 would
simply 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 be
in for some problematic behavior. Before seeing the problem firsthand, assume you have defined
the 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 if
you have acquired two abstract methods named Draw(). However, as the Line class offers a concrete
implementation, the compiler is happy to call the same implementation from an interface or object
reference.
    The explicit interface implementation, 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. To illustrate, here is the updated Line class:
         public class Line : Shape, IDraw3D
         {
         // You can only call this method using an IDraw3D interface reference.
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. 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 whole 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. Given this design, the caller is unable
to 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 of
interfaces that happen to contain identical methods. For example, assume you wish to create a class
that 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();
        }
Interfaces and Collections                                                                          213

If 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 provide
unique 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 AGENTS
As you already know, an abstract base class (containing abstract members) allows us to define a
specific behavior that is common across all members in the same class hierarchy. To really understand
the usefulness of interfaces, assume that the GetNumberOfPoints() method was not defined by
IPointy, but rather as an abstract member of the Shape class. If this were the case, Hexagon and
Triangle would still be able to return the correct result. At the same time, Circle would also be
obligated to contend with the GetNumberOfPoints() method as well. In this case, however, it might
seem a bit inelegant to simply return 0.
    The problem with defining GetNumberOfPoints() in the Shapes base class is that all derived
types must contend with this member, regardless of the semantics. Thus, the first key point about
interfaces 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 are
not within the same class hierarchy in the first place. 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 each hierarchy of types is completely
unrelated from a classical inheritance point of view, you can link them together using the common
behavior supplied by the IPointy interface:
       // This array can only contain types which implement the IPointy interface.
       IPointy myPointyObjects[] = {new Hexagon(), new Knife(),new Triangle(), new
Fork(), new PitchFork()};
At this point, 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. In fact, the IDisposable interface
may be implemented by numerous .NET types, regardless of which assembly they reside in. At
runtime, 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.
214                                                                               C# .Netprogramming


8.7 BUILDING INTERFACE HIERARCHIES:
Just as a class can serve as a base class to other classes (base classes to yet another class), it is
possible to build derived relationships among interfaces. As you might expect, the topmost interface
defines a general behavior, while the most derived interface defines more specific behaviors. To
illustrate, 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 I
demonstrate how to make use of a common COM-centric naming convention, which is to suffix a
numerical qualifier to the derived type. The relationships between these custom interfaces can be
seen in Figure .
    Now, if a class wished to support each behavior expressed in this interface hierarchy, it would
derive 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 force
the user to obtain the interface first.
           void IDraw.Draw() { // Basic drawing logic }
           void IDraw2.DrawToPrinter() { // Draw to printer. }
           void IDraw3.DrawToMetaFile() { // Draw to metafile. }
       }
Interfaces and Collections                                                                       215

Here 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 INTERFACES
As you build interface hierarchies, be aware that it is completely permissible to create an interface
that derives from multiple base interfaces. Recall, of course, that it is not permissible to build a
class that derives from multiple base classes. For example, assume you are building a new set of
interfaces 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 for
implementing TurboBoost(), Dive(), and Drive():
       public class JBCar : IJamesBondCar
       {
           public JBCar(){ }
           // Again, we are not required to use explicit interface implementation, as
we have no name clashes.
           void IBasicCar.Drive(){ Console.WriteLine(“Speeding up...”);}
           void IUnderwaterCar.Dive(){ Console.WriteLine(“Submerging...”);}
           void IJamesBondCar.TurboBoost(){ Console.WriteLine(“Blast off!”);}
       }
216                                                                              C# .Netprogramming

This 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 .NET
Although interface-based programming is a very powerful programming technique, one drawback
is the very simple fact that you are required to do a lot of manual typing. Given that interfaces are
a 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 .NET
does support an integrated tool that helps make this task easy. To illustrate, assume you have an
interface 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 you
wish to force VS .NET, switch to Class View and find a class that already specifies support for the
interface you wish to implement. Once you do, expand the Bases and Interfaces node, right-click
the interface icon, and select Add | Implement Interface Figure.




                                  Figure 8.5 : interface implementation
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 Class
View, you are able to specify the set of implemented interfaces at the time of creation. When you do
so, 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 fly
using language-agnostic terms.
    Majority of the intrinsic data types of C# (bool, int, double, etc.) are simply aliases to true-blue
structures in the System namespace. Like any .NET type, each of these structures may define any
number 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 find
this type (as well as all intrinsic data type structures) implements the IComparable and IConvertible
interfaces:
         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();
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() Members
I Convertible interface defines a number of methods of the form ToXXXX(), which as you can
most likely tell, provide a way to convert from one type into another. As you may also be able to
tell, it may not always be possible to convert between data types. For example, although it is natural
to predict converting from an Int32 into a Double, it really makes no sense to convert from a
Boolean into a DateTime.
    Given the fact that when a type implements an interface, it must contend with all methods (even
if they are not applicable), the system types will simply throw an InvalidCastException if the
conversion is semantically not well formed:
        // Obtain the IConvertible interface.
        bool myBool = true;
        IConvertible itfConv = (IConvertible)myBool;
        try{
        itfConv.ToSingle(...);
Interfaces and Collections                                                                       219

         }
         catch(InvalidCastException e) {
         Console.WriteLine(e);
         }

8.10.2 A Brief Word Regarding IFormatProvider
Notice that all of the ToXXXX() methods take a parameter of type IFormatProvider. Objects that
implement this interface are able to format their contents based on culture-specific information (for
example, returning a floating point number that is formatted in various currencies). Here is the
formal definition:
         public interface IFormatProvider
         {
         object GetFormat(Type formatType);
         }
If you were to build a custom type that should be formatted using various locals, implementing
IFormatProvider would be a must. However, if you are simply attempting to call members of the
base class libraries that require an IFormatProvider-compatible object, feel free to leverage the
System.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 to
programmatically discover a value that represents the type code of the type, which is represented
by 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 your
day-today programming endeavors.
220                                                                             C# .Netprogramming


8.11 BUILDING A CUSTOM ENUMERATOR
     (IENUMERABLE AND ENUMERATOR)
To illustrate the process of implementing existing .NET interfaces, let’s first examine the role of
IEnumerable and IEnumerator. Assume you have developed a class named Garage that contains a
set of individual Car types stored within a System.Array:
        // Garage contains a set of Car objects.
        public class Garage {
            private Car[] carArray;
            // Fill with some Car objects upon startup.
            public Garage() {
                carArray = new Car[4];
                carArray[0] = new Car(“Rusty”, 30);
                carArray[1] = new Car(“Clunker”, 55);
                carArray[2] = new Car(“Zippy”, 30);
                carArray[3] = new Car(“Fred”, 30);
            }
        }
Ideally, it would be convenient to iterate over the Garage object’s subitems using the C# foreach
construct:
        // 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 named
GetEnumerator(). This 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();
       }
Interfaces and Collections                                                                   221

As you can see, 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 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 implement
each 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# foreach
construct. Furthermore, given that the GetEnumerator() method has been defined publicly, the
object user could also interact with the IEnumerator type:
         // Manually work with IEnumerator.
         IEnumerator i = carLot.GetEnumerator();
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 use
of explicit interface implementation:
        public IEnumerator IEnumerable.GetEnumerator()
        {
        // Return the array object’s IEnumerator.
        return carArray.GetEnumerator();
        }

8.12 UNDERSTANDING C# ITERATOR METHODS
Under .NET 1.x, if you wished to have your custom collections (such as Garage) support foreach
like enumeration, implementing the IEnumerable interface (and possibly the IEnumerator interface)
was mandatory. However, offers an alternative way to build types that work with the foreach loop
via iterators.
    Simply put, an iterator is a member that specifies how a container’s internal items should be
returned when processed by foreach. While the iterator method must still be named GetEnumerator(),
and the return value must still be of type IEnumerator, your custom class does not need to implement
any of the expected interfaces:
        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.
Interfaces and Collections                                                                         223

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


8.13 BUILDING CLONEABLE OBJECTS ( 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:
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 standard
ICloneable 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, and
return 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.
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 following
code:
         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 of
the 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 the
references to those objects. If you wish to support a true deep copy, you will need to create a new
instance of any reference type variables during the cloning process. Let’s see an example.

A More Elaborate Cloning Example
Now 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 a
System.Guid. Here is the implementation:
         // This class describes a point.
         public class PointDescription
         {
             // Exposed publicly for simplicity.
             public string petName;
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 a
clone using the current implementation, a shallow (member-by-member) copy is achieved. To
illustrate, 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:”);
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: output

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. 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;
         }
228                                                                           C# .Netprogramming

If you rerun the application once again as shown in Figure , you see that the Point returned from
Clone() does copy its internal reference type member variables (note the pet name is now unique
for 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 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);
       }
Let’s assume you have updated the Car class to maintain an internal ID number (represented by a
simple integer named carID) that can be set via a constructor parameter and manipulated using a
new 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)
        {
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 an
array 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 build
custom 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 current
instance based on a specific point of data. The return value of CompareTo() is used to discover if
this type is less than, greater than, or equal to the object it is being compared with (see Table).
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 the
following 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.
Interfaces and Collections                                                                        231

8.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 sort
order. if you wanted to build a Car that could be sorted by ID as well as by pet name then you need
to make friends with another standard interface named IComparer, defined within the
System.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 are
trying 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 overloaded
Sort() 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);
         ...
         }
232                                                                                        C# .Netprogramming


8.16 EXPLORING THE SYSTEM. COLLECTIONS NAMESPACE
First of all, System.Collections defines a number of standard interfaces. Most of the classes defined
within the System.Collections namespace implement these interfaces to provide access to their
contents. 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-alone
entities. Figure illustrates the relationship between each type.




                                     Figure 8.8: Interface hierarchy
Interfaces and Collections                                                                       233

8.16.1 Role of 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. In a nutshell, 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, as well as (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);
         }


8.16.2 Role of IDictionary
As you may already be aware, 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 defines a Keys and Values property as well
as Add(), Remove(), and Contains() methods. The individual items may be obtained by the type
indexer. 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);
         }
234                                                                              C# .Netprogramming


8.16.3 Role of 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; }
        }
Notice how IDictionaryEnumerator allows you to enumerate over items in the dictionary via the
generic 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 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);
        }


8.17 CLASS TYPES OF SYSTEM.COLLECTIONS
As I hope you understand by this point in the chapter, interfaces by themselves are not very useful
until they are implemented by a given class or structure. Table provides a rundown of the core
classes in the System.Collections namespace and the key interfaces they support.
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 Type
The ArrayList type it allows you to dynamically resize the contents at your notion. To illustrate the
basics of this type, consider the following code, which shows 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);
            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();
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 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.

                                              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 a
Queue object that simulates a line of cars waiting to enter a car wash. First, assume the following
static 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());
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() 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.

8.17.3 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);}
         }
238                                                                                        C# .Netprogramming

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 eventually
empty, at which time additional Peek()/Pop() calls raise a system exception.


8.18 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. For example,
the StringDictionary and ListDictionary types each provide a stylized implementation of the
IDictionary 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.


                                              SUMMARY
The .NET framework defines a collection namespace of system .which contains many classes and
interfaces, provides to define various collection of objects every programmer usage this class and
interfaces 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.
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?

More Related Content

What's hot (20)

Interfaces c#
Interfaces c#Interfaces c#
Interfaces c#
 
Wrapper class
Wrapper classWrapper class
Wrapper class
 
Java string handling
Java string handlingJava string handling
Java string handling
 
Pointers, virtual function and polymorphism
Pointers, virtual function and polymorphismPointers, virtual function and polymorphism
Pointers, virtual function and polymorphism
 
DESIGN PATTERNS: Strategy Patterns
DESIGN PATTERNS: Strategy PatternsDESIGN PATTERNS: Strategy Patterns
DESIGN PATTERNS: Strategy Patterns
 
Operator overloading
Operator overloadingOperator overloading
Operator overloading
 
interface in c#
interface in c#interface in c#
interface in c#
 
Arrays C#
Arrays C#Arrays C#
Arrays C#
 
Modern c++ (C++ 11/14)
Modern c++ (C++ 11/14)Modern c++ (C++ 11/14)
Modern c++ (C++ 11/14)
 
16 virtual function
16 virtual function16 virtual function
16 virtual function
 
Structure in c sharp
Structure in c sharpStructure in c sharp
Structure in c sharp
 
Java Collections
Java CollectionsJava Collections
Java Collections
 
array of object pointer in c++
array of object pointer in c++array of object pointer in c++
array of object pointer in c++
 
Multithreading in java
Multithreading in javaMultithreading in java
Multithreading in java
 
C# Lab Programs.pdf
C# Lab Programs.pdfC# Lab Programs.pdf
C# Lab Programs.pdf
 
C# 101: Intro to Programming with C#
C# 101: Intro to Programming with C#C# 101: Intro to Programming with C#
C# 101: Intro to Programming with C#
 
Interfaces in java
Interfaces in javaInterfaces in java
Interfaces in java
 
Ti1220 Lecture 2: Names, Bindings, and Scopes
Ti1220 Lecture 2: Names, Bindings, and ScopesTi1220 Lecture 2: Names, Bindings, and Scopes
Ti1220 Lecture 2: Names, Bindings, and Scopes
 
Method overloading and constructor overloading in java
Method overloading and constructor overloading in javaMethod overloading and constructor overloading in java
Method overloading and constructor overloading in java
 
Recursion in C++
Recursion in C++Recursion in C++
Recursion in C++
 

Viewers also liked (7)

Fazenda Modelo
Fazenda ModeloFazenda Modelo
Fazenda Modelo
 
Alcat powerpoint
Alcat powerpointAlcat powerpoint
Alcat powerpoint
 
Pulp paper-pollution
Pulp paper-pollutionPulp paper-pollution
Pulp paper-pollution
 
Presentacion
PresentacionPresentacion
Presentacion
 
Biogas from-cane-stalk
Biogas from-cane-stalkBiogas from-cane-stalk
Biogas from-cane-stalk
 
Indian paper industry-1990-2002-
Indian paper industry-1990-2002-Indian paper industry-1990-2002-
Indian paper industry-1990-2002-
 
Plantation tree-crops-kerala-india
Plantation tree-crops-kerala-indiaPlantation tree-crops-kerala-india
Plantation tree-crops-kerala-india
 

Similar to Dotnet unit 4

Chapter27 polymorphism-virtual-function-abstract-class
Chapter27 polymorphism-virtual-function-abstract-classChapter27 polymorphism-virtual-function-abstract-class
Chapter27 polymorphism-virtual-function-abstract-classDeepak Singh
 
Lecture 3, c++(complete reference,herbet sheidt)chapter-13
Lecture 3, c++(complete reference,herbet sheidt)chapter-13Lecture 3, c++(complete reference,herbet sheidt)chapter-13
Lecture 3, c++(complete reference,herbet sheidt)chapter-13Abu Saleh
 
Uncommon Design Patterns
Uncommon Design PatternsUncommon Design Patterns
Uncommon Design PatternsStefano Fago
 
4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)
4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)
4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)PROIDEA
 
CLASSES, STRUCTURE,UNION in C++
CLASSES, STRUCTURE,UNION in C++CLASSES, STRUCTURE,UNION in C++
CLASSES, STRUCTURE,UNION in C++Prof Ansari
 
Example for Abstract Class and Interface.pdf
Example for Abstract Class and Interface.pdfExample for Abstract Class and Interface.pdf
Example for Abstract Class and Interface.pdfrajaratna4
 
Creat Shape classes from scratch DETAILS You will create 3 shape cla.pdf
Creat Shape classes from scratch DETAILS You will create 3 shape cla.pdfCreat Shape classes from scratch DETAILS You will create 3 shape cla.pdf
Creat Shape classes from scratch DETAILS You will create 3 shape cla.pdfaromanets
 
CSharp presentation and software developement
CSharp presentation and software developementCSharp presentation and software developement
CSharp presentation and software developementfrwebhelp
 
Unit 1 Part - 3 constructor Overloading Static.ppt
Unit 1 Part - 3  constructor Overloading Static.pptUnit 1 Part - 3  constructor Overloading Static.ppt
Unit 1 Part - 3 constructor Overloading Static.pptDeepVala5
 
OO-like C Programming: Struct Inheritance and Virtual Function
OO-like C Programming: Struct Inheritance and Virtual FunctionOO-like C Programming: Struct Inheritance and Virtual Function
OO-like C Programming: Struct Inheritance and Virtual FunctionYu-Sheng (Yosen) Chen
 
Lecture02-OOP-Review.ppt
Lecture02-OOP-Review.pptLecture02-OOP-Review.ppt
Lecture02-OOP-Review.ppt02LabiqaIslam
 
Advance features of C++
Advance features of C++Advance features of C++
Advance features of C++vidyamittal
 
Cppt 101102014428-phpapp01
Cppt 101102014428-phpapp01Cppt 101102014428-phpapp01
Cppt 101102014428-phpapp01Getachew Ganfur
 

Similar to Dotnet unit 4 (20)

Synapseindia dot net development
Synapseindia dot net developmentSynapseindia dot net development
Synapseindia dot net development
 
Chapter27 polymorphism-virtual-function-abstract-class
Chapter27 polymorphism-virtual-function-abstract-classChapter27 polymorphism-virtual-function-abstract-class
Chapter27 polymorphism-virtual-function-abstract-class
 
Lecture 3, c++(complete reference,herbet sheidt)chapter-13
Lecture 3, c++(complete reference,herbet sheidt)chapter-13Lecture 3, c++(complete reference,herbet sheidt)chapter-13
Lecture 3, c++(complete reference,herbet sheidt)chapter-13
 
Uncommon Design Patterns
Uncommon Design PatternsUncommon Design Patterns
Uncommon Design Patterns
 
4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)
4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)
4Developers 2018: Ile (nie) wiesz o strukturach w .NET (Łukasz Pyrzyk)
 
CLASSES, STRUCTURE,UNION in C++
CLASSES, STRUCTURE,UNION in C++CLASSES, STRUCTURE,UNION in C++
CLASSES, STRUCTURE,UNION in C++
 
OOP Core Concept
OOP Core ConceptOOP Core Concept
OOP Core Concept
 
Csharp_mahesh
Csharp_maheshCsharp_mahesh
Csharp_mahesh
 
02.adt
02.adt02.adt
02.adt
 
Example for Abstract Class and Interface.pdf
Example for Abstract Class and Interface.pdfExample for Abstract Class and Interface.pdf
Example for Abstract Class and Interface.pdf
 
Creat Shape classes from scratch DETAILS You will create 3 shape cla.pdf
Creat Shape classes from scratch DETAILS You will create 3 shape cla.pdfCreat Shape classes from scratch DETAILS You will create 3 shape cla.pdf
Creat Shape classes from scratch DETAILS You will create 3 shape cla.pdf
 
CSharp presentation and software developement
CSharp presentation and software developementCSharp presentation and software developement
CSharp presentation and software developement
 
Unit 1 Part - 3 constructor Overloading Static.ppt
Unit 1 Part - 3  constructor Overloading Static.pptUnit 1 Part - 3  constructor Overloading Static.ppt
Unit 1 Part - 3 constructor Overloading Static.ppt
 
Op ps
Op psOp ps
Op ps
 
OO-like C Programming: Struct Inheritance and Virtual Function
OO-like C Programming: Struct Inheritance and Virtual FunctionOO-like C Programming: Struct Inheritance and Virtual Function
OO-like C Programming: Struct Inheritance and Virtual Function
 
polymorphism.ppt
polymorphism.pptpolymorphism.ppt
polymorphism.ppt
 
Lecture02-OOP-Review.ppt
Lecture02-OOP-Review.pptLecture02-OOP-Review.ppt
Lecture02-OOP-Review.ppt
 
Cpp tutorial
Cpp tutorialCpp tutorial
Cpp tutorial
 
Advance features of C++
Advance features of C++Advance features of C++
Advance features of C++
 
Cppt 101102014428-phpapp01
Cppt 101102014428-phpapp01Cppt 101102014428-phpapp01
Cppt 101102014428-phpapp01
 

Recently uploaded

SOCIAL AND HISTORICAL CONTEXT - LFTVD.pptx
SOCIAL AND HISTORICAL CONTEXT - LFTVD.pptxSOCIAL AND HISTORICAL CONTEXT - LFTVD.pptx
SOCIAL AND HISTORICAL CONTEXT - LFTVD.pptxiammrhaywood
 
MENTAL STATUS EXAMINATION format.docx
MENTAL     STATUS EXAMINATION format.docxMENTAL     STATUS EXAMINATION format.docx
MENTAL STATUS EXAMINATION format.docxPoojaSen20
 
Paris 2024 Olympic Geographies - an activity
Paris 2024 Olympic Geographies - an activityParis 2024 Olympic Geographies - an activity
Paris 2024 Olympic Geographies - an activityGeoBlogs
 
BASLIQ CURRENT LOOKBOOK LOOKBOOK(1) (1).pdf
BASLIQ CURRENT LOOKBOOK  LOOKBOOK(1) (1).pdfBASLIQ CURRENT LOOKBOOK  LOOKBOOK(1) (1).pdf
BASLIQ CURRENT LOOKBOOK LOOKBOOK(1) (1).pdfSoniaTolstoy
 
microwave assisted reaction. General introduction
microwave assisted reaction. General introductionmicrowave assisted reaction. General introduction
microwave assisted reaction. General introductionMaksud Ahmed
 
The Most Excellent Way | 1 Corinthians 13
The Most Excellent Way | 1 Corinthians 13The Most Excellent Way | 1 Corinthians 13
The Most Excellent Way | 1 Corinthians 13Steve Thomason
 
Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...
Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...
Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...EduSkills OECD
 
How to Make a Pirate ship Primary Education.pptx
How to Make a Pirate ship Primary Education.pptxHow to Make a Pirate ship Primary Education.pptx
How to Make a Pirate ship Primary Education.pptxmanuelaromero2013
 
Kisan Call Centre - To harness potential of ICT in Agriculture by answer farm...
Kisan Call Centre - To harness potential of ICT in Agriculture by answer farm...Kisan Call Centre - To harness potential of ICT in Agriculture by answer farm...
Kisan Call Centre - To harness potential of ICT in Agriculture by answer farm...Krashi Coaching
 
POINT- BIOCHEMISTRY SEM 2 ENZYMES UNIT 5.pptx
POINT- BIOCHEMISTRY SEM 2 ENZYMES UNIT 5.pptxPOINT- BIOCHEMISTRY SEM 2 ENZYMES UNIT 5.pptx
POINT- BIOCHEMISTRY SEM 2 ENZYMES UNIT 5.pptxSayali Powar
 
Solving Puzzles Benefits Everyone (English).pptx
Solving Puzzles Benefits Everyone (English).pptxSolving Puzzles Benefits Everyone (English).pptx
Solving Puzzles Benefits Everyone (English).pptxOH TEIK BIN
 
Presiding Officer Training module 2024 lok sabha elections
Presiding Officer Training module 2024 lok sabha electionsPresiding Officer Training module 2024 lok sabha elections
Presiding Officer Training module 2024 lok sabha electionsanshu789521
 
Q4-W6-Restating Informational Text Grade 3
Q4-W6-Restating Informational Text Grade 3Q4-W6-Restating Informational Text Grade 3
Q4-W6-Restating Informational Text Grade 3JemimahLaneBuaron
 
The basics of sentences session 2pptx copy.pptx
The basics of sentences session 2pptx copy.pptxThe basics of sentences session 2pptx copy.pptx
The basics of sentences session 2pptx copy.pptxheathfieldcps1
 
URLs and Routing in the Odoo 17 Website App
URLs and Routing in the Odoo 17 Website AppURLs and Routing in the Odoo 17 Website App
URLs and Routing in the Odoo 17 Website AppCeline George
 
Arihant handbook biology for class 11 .pdf
Arihant handbook biology for class 11 .pdfArihant handbook biology for class 11 .pdf
Arihant handbook biology for class 11 .pdfchloefrazer622
 
Incoming and Outgoing Shipments in 1 STEP Using Odoo 17
Incoming and Outgoing Shipments in 1 STEP Using Odoo 17Incoming and Outgoing Shipments in 1 STEP Using Odoo 17
Incoming and Outgoing Shipments in 1 STEP Using Odoo 17Celine George
 
Crayon Activity Handout For the Crayon A
Crayon Activity Handout For the Crayon ACrayon Activity Handout For the Crayon A
Crayon Activity Handout For the Crayon AUnboundStockton
 
“Oh GOSH! Reflecting on Hackteria's Collaborative Practices in a Global Do-It...
“Oh GOSH! Reflecting on Hackteria's Collaborative Practices in a Global Do-It...“Oh GOSH! Reflecting on Hackteria's Collaborative Practices in a Global Do-It...
“Oh GOSH! Reflecting on Hackteria's Collaborative Practices in a Global Do-It...Marc Dusseiller Dusjagr
 

Recently uploaded (20)

SOCIAL AND HISTORICAL CONTEXT - LFTVD.pptx
SOCIAL AND HISTORICAL CONTEXT - LFTVD.pptxSOCIAL AND HISTORICAL CONTEXT - LFTVD.pptx
SOCIAL AND HISTORICAL CONTEXT - LFTVD.pptx
 
MENTAL STATUS EXAMINATION format.docx
MENTAL     STATUS EXAMINATION format.docxMENTAL     STATUS EXAMINATION format.docx
MENTAL STATUS EXAMINATION format.docx
 
Paris 2024 Olympic Geographies - an activity
Paris 2024 Olympic Geographies - an activityParis 2024 Olympic Geographies - an activity
Paris 2024 Olympic Geographies - an activity
 
BASLIQ CURRENT LOOKBOOK LOOKBOOK(1) (1).pdf
BASLIQ CURRENT LOOKBOOK  LOOKBOOK(1) (1).pdfBASLIQ CURRENT LOOKBOOK  LOOKBOOK(1) (1).pdf
BASLIQ CURRENT LOOKBOOK LOOKBOOK(1) (1).pdf
 
microwave assisted reaction. General introduction
microwave assisted reaction. General introductionmicrowave assisted reaction. General introduction
microwave assisted reaction. General introduction
 
The Most Excellent Way | 1 Corinthians 13
The Most Excellent Way | 1 Corinthians 13The Most Excellent Way | 1 Corinthians 13
The Most Excellent Way | 1 Corinthians 13
 
Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...
Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...
Presentation by Andreas Schleicher Tackling the School Absenteeism Crisis 30 ...
 
How to Make a Pirate ship Primary Education.pptx
How to Make a Pirate ship Primary Education.pptxHow to Make a Pirate ship Primary Education.pptx
How to Make a Pirate ship Primary Education.pptx
 
Kisan Call Centre - To harness potential of ICT in Agriculture by answer farm...
Kisan Call Centre - To harness potential of ICT in Agriculture by answer farm...Kisan Call Centre - To harness potential of ICT in Agriculture by answer farm...
Kisan Call Centre - To harness potential of ICT in Agriculture by answer farm...
 
POINT- BIOCHEMISTRY SEM 2 ENZYMES UNIT 5.pptx
POINT- BIOCHEMISTRY SEM 2 ENZYMES UNIT 5.pptxPOINT- BIOCHEMISTRY SEM 2 ENZYMES UNIT 5.pptx
POINT- BIOCHEMISTRY SEM 2 ENZYMES UNIT 5.pptx
 
Solving Puzzles Benefits Everyone (English).pptx
Solving Puzzles Benefits Everyone (English).pptxSolving Puzzles Benefits Everyone (English).pptx
Solving Puzzles Benefits Everyone (English).pptx
 
Presiding Officer Training module 2024 lok sabha elections
Presiding Officer Training module 2024 lok sabha electionsPresiding Officer Training module 2024 lok sabha elections
Presiding Officer Training module 2024 lok sabha elections
 
Q4-W6-Restating Informational Text Grade 3
Q4-W6-Restating Informational Text Grade 3Q4-W6-Restating Informational Text Grade 3
Q4-W6-Restating Informational Text Grade 3
 
The basics of sentences session 2pptx copy.pptx
The basics of sentences session 2pptx copy.pptxThe basics of sentences session 2pptx copy.pptx
The basics of sentences session 2pptx copy.pptx
 
URLs and Routing in the Odoo 17 Website App
URLs and Routing in the Odoo 17 Website AppURLs and Routing in the Odoo 17 Website App
URLs and Routing in the Odoo 17 Website App
 
Arihant handbook biology for class 11 .pdf
Arihant handbook biology for class 11 .pdfArihant handbook biology for class 11 .pdf
Arihant handbook biology for class 11 .pdf
 
Incoming and Outgoing Shipments in 1 STEP Using Odoo 17
Incoming and Outgoing Shipments in 1 STEP Using Odoo 17Incoming and Outgoing Shipments in 1 STEP Using Odoo 17
Incoming and Outgoing Shipments in 1 STEP Using Odoo 17
 
Crayon Activity Handout For the Crayon A
Crayon Activity Handout For the Crayon ACrayon Activity Handout For the Crayon A
Crayon Activity Handout For the Crayon A
 
Model Call Girl in Bikash Puri Delhi reach out to us at 🔝9953056974🔝
Model Call Girl in Bikash Puri  Delhi reach out to us at 🔝9953056974🔝Model Call Girl in Bikash Puri  Delhi reach out to us at 🔝9953056974🔝
Model Call Girl in Bikash Puri Delhi reach out to us at 🔝9953056974🔝
 
“Oh GOSH! Reflecting on Hackteria's Collaborative Practices in a Global Do-It...
“Oh GOSH! Reflecting on Hackteria's Collaborative Practices in a Global Do-It...“Oh GOSH! Reflecting on Hackteria's Collaborative Practices in a Global Do-It...
“Oh GOSH! Reflecting on Hackteria's Collaborative Practices in a Global Do-It...
 

Dotnet unit 4

  • 1. Chapter 8 Interfaces and Collections 8.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 behavior that 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 .NET types, interfaces never specify a base class (not even System.Object) and contain members that do not take an access modifier (interface methods are implicitly public). To get the ball rolling, here is a 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. Interfaces and Collections 205 8.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 listed first. // This class derives from System.Object and implements a single interface. public class SomeClass : ISomeInterface {...} // This class derives from a specific base class and implements a single interface. public class AnotherClass : MyBaseClass, ISomeInterface {...} // This struct derives from System.ValueType and implements a single 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. 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, but may 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 to do so.
  • 4. Interfaces and Collections 207 8.3.1 Invoking Interface Members at the object Level The way to interact with functionality supplied by a given interface is to invoke the methods directly from 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 has implemented the interface. There is number of situation you will be required to obtain a valid interface reference directly before you are able to activate said functionality. 8.3.2 Obtaining Interface References: Explicit Casting Assume you have created an instance of the Hexagon class, and wish to dynamically see if it supports the pointy behavior. One approach is to make use of an explicit cast in order to obtain an interface 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 a single 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 IPointy interface. If the object does support this interface, you are then able to exercise the behavior accordingly. When you attempt to access an interface not supported by a given class using a direct cast, the runtime throws an InvalidCastException. To safely recover from this possibility, simply catch the exception: 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. 208 C# .Netprogramming 8.3.3 Obtaining Interface References: The “as” Keyword The 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 supported by the object (notice that you check your IPointy reference for null before continuing) rather than throwing an exception. 8.3.4 Obtaining Interface References: The “is” Keyword Finally, you may also obtain an interface from an object using the “is” keyword. If the object in question 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 Hierarchy Create 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 array support 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 how to draw themselves. s[i].Draw(); // Who’s pointy? if(s[i]is IPointy)
  • 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: output 8.4 INTERFACES AS PARAMETERS Given that interfaces are strongly typed entities, you may construct methods that take interfaces as parameters as well as method return values. To illustrate, assume you have defined another interface named 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 been configured to support this new behavior:
  • 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 of the 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 effectively send 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 IMPLEMENTATION In our previous definition of IDraw3D, you were forced to name your method Draw3D() in order to avoid clashing with the abstract Draw() method defined in the Shapes base class:
  • 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 would simply 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 be in for some problematic behavior. Before seeing the problem firsthand, assume you have defined the 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 if you have acquired two abstract methods named Draw(). However, as the Line class offers a concrete implementation, the compiler is happy to call the same implementation from an interface or object reference. The explicit interface implementation, 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. 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. 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. 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 whole 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. Given this design, the caller is unable to 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 of interfaces that happen to contain identical methods. For example, assume you wish to create a class that 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. Interfaces and Collections 213 If 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 provide unique 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 AGENTS As you already know, an abstract base class (containing abstract members) allows us to define a specific behavior that is common across all members in the same class hierarchy. To really understand the usefulness of interfaces, assume that the GetNumberOfPoints() method was not defined by IPointy, but rather as an abstract member of the Shape class. If this were the case, Hexagon and Triangle would still be able to return the correct result. At the same time, Circle would also be obligated to contend with the GetNumberOfPoints() method as well. In this case, however, it might seem a bit inelegant to simply return 0. The problem with defining GetNumberOfPoints() in the Shapes base class is that all derived types must contend with this member, regardless of the semantics. Thus, the first key point about interfaces 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 are not within the same class hierarchy in the first place. 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 each hierarchy of types is completely unrelated from a classical inheritance point of view, you can link them together using the common behavior supplied by the IPointy interface: // This array can only contain types which implement the IPointy interface. IPointy myPointyObjects[] = {new Hexagon(), new Knife(),new Triangle(), new Fork(), new PitchFork()}; At this point, 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. In fact, the IDisposable interface may be implemented by numerous .NET types, regardless of which assembly they reside in. At runtime, 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. 214 C# .Netprogramming 8.7 BUILDING INTERFACE HIERARCHIES: Just as a class can serve as a base class to other classes (base classes to yet another class), it is possible to build derived relationships among interfaces. As you might expect, the topmost interface defines a general behavior, while the most derived interface defines more specific behaviors. To illustrate, 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 I demonstrate how to make use of a common COM-centric naming convention, which is to suffix a numerical qualifier to the derived type. The relationships between these custom interfaces can be seen in Figure . Now, if a class wished to support each behavior expressed in this interface hierarchy, it would derive 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 force the 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. Interfaces and Collections 215 Here 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 INTERFACES As you build interface hierarchies, be aware that it is completely permissible to create an interface that derives from multiple base interfaces. Recall, of course, that it is not permissible to build a class that derives from multiple base classes. For example, assume you are building a new set of interfaces 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 for implementing TurboBoost(), Dive(), and Drive(): public class JBCar : IJamesBondCar { public JBCar(){ } // Again, we are not required to use explicit interface implementation, as we 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. 216 C# .Netprogramming This 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 .NET Although interface-based programming is a very powerful programming technique, one drawback is the very simple fact that you are required to do a lot of manual typing. Given that interfaces are a 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 .NET does support an integrated tool that helps make this task easy. To illustrate, assume you have an interface 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 you wish to force VS .NET, switch to Class View and find a class that already specifies support for the interface you wish to implement. Once you do, expand the Bases and Interfaces node, right-click the interface icon, and select Add | Implement Interface Figure. Figure 8.5 : interface implementation
  • 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 Class View, you are able to specify the set of implemented interfaces at the time of creation. When you do so, 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 fly using language-agnostic terms. Majority of the intrinsic data types of C# (bool, int, double, etc.) are simply aliases to true-blue structures in the System namespace. Like any .NET type, each of these structures may define any number 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 find this type (as well as all intrinsic data type structures) implements the IComparable and IConvertible interfaces: 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. 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() Members I Convertible interface defines a number of methods of the form ToXXXX(), which as you can most likely tell, provide a way to convert from one type into another. As you may also be able to tell, it may not always be possible to convert between data types. For example, although it is natural to predict converting from an Int32 into a Double, it really makes no sense to convert from a Boolean into a DateTime. Given the fact that when a type implements an interface, it must contend with all methods (even if they are not applicable), the system types will simply throw an InvalidCastException if the conversion is semantically not well formed: // Obtain the IConvertible interface. bool myBool = true; IConvertible itfConv = (IConvertible)myBool; try{ itfConv.ToSingle(...);
  • 16. Interfaces and Collections 219 } catch(InvalidCastException e) { Console.WriteLine(e); } 8.10.2 A Brief Word Regarding IFormatProvider Notice that all of the ToXXXX() methods take a parameter of type IFormatProvider. Objects that implement this interface are able to format their contents based on culture-specific information (for example, returning a floating point number that is formatted in various currencies). Here is the formal definition: public interface IFormatProvider { object GetFormat(Type formatType); } If you were to build a custom type that should be formatted using various locals, implementing IFormatProvider would be a must. However, if you are simply attempting to call members of the base class libraries that require an IFormatProvider-compatible object, feel free to leverage the System.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 to programmatically discover a value that represents the type code of the type, which is represented by 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 your day-today programming endeavors.
  • 17. 220 C# .Netprogramming 8.11 BUILDING A CUSTOM ENUMERATOR (IENUMERABLE AND ENUMERATOR) To illustrate the process of implementing existing .NET interfaces, let’s first examine the role of IEnumerable and IEnumerator. Assume you have developed a class named Garage that contains a set of individual Car types stored within a System.Array: // Garage contains a set of Car objects. public class Garage { private Car[] carArray; // Fill with some Car objects upon startup. public Garage() { carArray = new Car[4]; carArray[0] = new Car(“Rusty”, 30); carArray[1] = new Car(“Clunker”, 55); carArray[2] = new Car(“Zippy”, 30); carArray[3] = new Car(“Fred”, 30); } } Ideally, it would be convenient to iterate over the Garage object’s subitems using the C# foreach construct: // 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 named GetEnumerator(). This 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(); }
  • 18. Interfaces and Collections 221 As you can see, 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 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 implement each 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# foreach construct. Furthermore, given that the GetEnumerator() method has been defined publicly, the object user could also interact with the IEnumerator type: // Manually work with IEnumerator. IEnumerator i = carLot.GetEnumerator();
  • 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 use of explicit interface implementation: public IEnumerator IEnumerable.GetEnumerator() { // Return the array object’s IEnumerator. return carArray.GetEnumerator(); } 8.12 UNDERSTANDING C# ITERATOR METHODS Under .NET 1.x, if you wished to have your custom collections (such as Garage) support foreach like enumeration, implementing the IEnumerable interface (and possibly the IEnumerator interface) was mandatory. However, offers an alternative way to build types that work with the foreach loop via iterators. Simply put, an iterator is a member that specifies how a container’s internal items should be returned when processed by foreach. While the iterator method must still be named GetEnumerator(), and the return value must still be of type IEnumerator, your custom class does not need to implement any of the expected interfaces: 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.
  • 20. Interfaces and Collections 223 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). 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 (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::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(); } 8.13 BUILDING CLONEABLE OBJECTS ( 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:
  • 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 standard ICloneable 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, and return 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. 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 following code: 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 of the 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 the references to those objects. If you wish to support a true deep copy, you will need to create a new instance of any reference type variables during the cloning process. Let’s see an example. A More Elaborate Cloning Example Now 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 a System.Guid. Here is the implementation: // This class describes a point. public class PointDescription { // Exposed publicly for simplicity. public string petName;
  • 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 a clone using the current implementation, a shallow (member-by-member) copy is achieved. To illustrate, 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. 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: output 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. 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. 228 C# .Netprogramming If you rerun the application once again as shown in Figure , you see that the Point returned from Clone() does copy its internal reference type member variables (note the pet name is now unique for 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 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); } Let’s assume you have updated the Car class to maintain an internal ID number (represented by a simple integer named carID) that can be set via a constructor parameter and manipulated using a new 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. 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 an array 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 build custom 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 current instance based on a specific point of data. The return value of CompareTo() is used to discover if this type is less than, greater than, or equal to the object it is being compared with (see Table).
  • 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 the following 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. Interfaces and Collections 231 8.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 sort order. if you wanted to build a Car that could be sorted by ID as well as by pet name then you need to make friends with another standard interface named IComparer, defined within the System.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 are trying 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 overloaded Sort() 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. 232 C# .Netprogramming 8.16 EXPLORING THE SYSTEM. COLLECTIONS NAMESPACE First of all, System.Collections defines a number of standard interfaces. Most of the classes defined within the System.Collections namespace implement these interfaces to provide access to their contents. 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-alone entities. Figure illustrates the relationship between each type. Figure 8.8: Interface hierarchy
  • 30. Interfaces and Collections 233 8.16.1 Role of 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. In a nutshell, 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, as well as (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); } 8.16.2 Role of IDictionary As you may already be aware, 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 defines a Keys and Values property as well as Add(), Remove(), and Contains() methods. The individual items may be obtained by the type indexer. 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. 234 C# .Netprogramming 8.16.3 Role of 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; } } Notice how IDictionaryEnumerator allows you to enumerate over items in the dictionary via the generic 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 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); } 8.17 CLASS TYPES OF SYSTEM.COLLECTIONS As I hope you understand by this point in the chapter, interfaces by themselves are not very useful until they are implemented by a given class or structure. Table provides a rundown of the core classes in the System.Collections namespace and the key interfaces they support.
  • 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 Type The ArrayList type it allows you to dynamically resize the contents at your notion. To illustrate the basics of this type, consider the following code, which shows 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); 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. 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 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. 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 a Queue object that simulates a line of cars waiting to enter a car wash. First, assume the following static 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. 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() 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. 8.17.3 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);} }
  • 35. 238 C# .Netprogramming 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 eventually empty, at which time additional Peek()/Pop() calls raise a system exception. 8.18 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. For example, the StringDictionary and ListDictionary types each provide a stylized implementation of the IDictionary 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. SUMMARY The .NET framework defines a collection namespace of system .which contains many classes and interfaces, provides to define various collection of objects every programmer usage this class and interfaces 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. 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?