Sunteți pe pagina 1din 16

CSC10210

Object-Oriented Program Development

Topic 6
Polymorphism, abstract classes and interfaces
©
Southern Cross University

Developed and produced by Southern Cross University


Military Rd, EAST LISMORE. N.S.W. 2480. 1st February, 2014.

No part of this publication may be reproduced, stored in a retrieval system or transmitted


in any form of by means electronic, mechanical, photocopying, recording or otherwise
without the prior written permission of the publisher.

Copyright materials reproduced herein are used under the provisions of the
Copyright Amendment Act (1989).

Readings, if any, indicated in this work have been copied under section VB of the Copyright Amendment Act 1989
Copies, if any, were made by Southern Cross University in February, 2012
for private study only by students.

CAL LICENSED COPY - UNAUTHORISED COPYING PROHIBITED


ISY00246 Study Guide Topic 6 – Polymorphism, abstract classes and interfaces

Contents

POLYMORPHISM, ABSTRACT CLASSES AND INTERFACES ...................................................................... 4


References ................................................................................................................................................................. 4
Objectives.................................................................................................................................................................. 4
Introduction ............................................................................................................................................................... 4
The Object class .................................................................................................................................................... 4
Polymorphism ........................................................................................................................................................... 7
Abstract classes ......................................................................................................................................................... 9
The sealed keyword ............................................................................................................................................ 11
Interfaces ................................................................................................................................................................. 11
Topic Summary ....................................................................................................................................................... 13
Answers to Activities .............................................................................................................................................. 14

6-3
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

Polymorphism, abstract classes and


interfaces
References
Deitel, P. & Deitel, H. 2014, Visual C# 2012 How to Program, 5th ed., Pearson International. (see
references throughout this topic)
OMG 2009. Unified Modeling Language, http://www.uml.org/
Miller, R. Practical UML: a hands on introduction for developers, Embarcadero Technologies,
http://dn.codegear.com/article/31863 (last accessed December 2008)

Objectives
On completion of this topic you should be able to:
1. Understand the role of the Object class.
2. Understand and use polymorphism in object-oriented systems.
3. Design and use abstract classes.
4. Use the sealed keyword.
5. Design and use interfaces.

Introduction
Polymorphism is one of the distinctive attributes of object-oriented programming languages. It adds
to the re-use provided by inheritance to allow the same code to be designed to process many different
(though related) objects. In this topic we will extend our knowledge of classes to see how
polymorphism works. We will also extend our basic knowledge of inheritance to the design of
abstract classes and interfaces which enhance polymorphism by providing special base classes to aid
us to produce polymorphic designs. We will start our discussion of polymorphism by studying the
system class Object and see how it may be used in a polymorphic way. We will then look as
abstract classes and interfaces which are language features specially designed to allow polymorphic
behavior.

The Object class


All C# programs use the System namespace. This namespace includes a special class called Object.
It is a special class because it is the base class for all declared classes that do not specify a base class.
If you think about that for a while you will realise that the Object class is the base class of any
inheritance hierarchy.
So for example, suppose we declare the following simple Person class which will contain just a
string for a person’s name.
class Person
{
private string name;

public Person(string n) { name = n; }


public string GetName() { return name; }
}

6-4
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

This is equivalent to the class header:


class Person : Object
{
private string name;

public Person(string n) { name = n; }

public string GetName() { return name; }


}
So if Object is the base class of all classes either directly or indirectly through inheritance
hierarchies, it will contain some methods that will be available in all classes. You will discover the
complete list of methods in the activity below. We will just discuss three of them here.
A useful method inherited from the Object class is ToString(). This method prints out the
object’s class name, including its namespace. So the following code sequence using the above
definition of Person class will access the inherited ToString() method.
class TestObject
{

public static void Main()


{
Person p = new Person(“Barry”);
Console.WriteLine(p.ToString();
}
}
This will print the following line on the console:
TestObject.Person
The C# compiler is aware of the ToString() function which is inherited by all objects. It can
automatically convert any object into a string by calling the ToString() member function. For
example, the WriteLine() function call above can be replaced by the following code which causes
the compiler to automatically call the ToString() member function printing the same line of text.
Console.WriteLine(p);
An additional property of the ToString() method is that is declared as virtual. You will recall
from Topic 2 that this means you can redefine the ToString() method in derived classes. So for
instance, we could redefine this method in the above Person class to print out the class name and the
value by inserting the following method definition in the Person class.
public override string ToString()
{
return string.Format("class Person; value \"{0}\".",name);
}
Execution of the TestObject program would then print out the following message on the console
instead of the default message:
class Person; value “Barry”.
The second useful method inherited by all classes is the Equals() method which can be used to
check if two objects are equivalent. The full signature of the most commonly used version of this
method is:
public boolean Equals(Object o)
Now the inherited implementation checks that the two objects are actually the same object in memory.
To demonstrate this method, the following code creates three variables (references) and two objects of
type Person. It then uses the Equals() method to check if the first method is the same as the
second and the third.
Person p = new Person("Barry");
Person p2 = new Person("other");
Person p3 = p;

6-5
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

if (p.Equals(p2))
Console.WriteLine("p and p2 are the same object");
if (p.Equals(p3))
Console.WriteLine("p and p3 are the same object");
When we execute this code only one line will be printed:
p and p3 are the same object
Make sure you understand why this is true. The diagrams introduced in Topic 2 may help you
understand this if you are not clear.
Although the inherited Equals() methods check that the objects being compared are the same object
in memory, this does not have to be the case. The Equals() method is also declared as virtual so
that classes may redefine it to compare objects in other ways. For example, the above Person class
above could redefine the Equals methods to compare name strings, which would mean that two
objects could be considered equal even if they represented different objects in memory. An example
coding for the above Person class would be as follows:
public override bool Equals(Object obj)
{
if (obj == null || GetType() != obj.GetType()) return false;
Person p = (Person)obj;
return (name == p.GetName());
}
This implementation is slightly complicated because the Equals() member function has an Object
parameter. We need to check that the parameter is actually a Person object before we check if the
name strings are equal (otherwise they may not have a name string!). Also in this example, we used
the GetType() member function which we look at next.
The third member function of the Object class that you may find useful is the GetType() member
function which returns an object that represents the class type of the object. We will not go into
details about the object returned but you may find this method used in code you examine. In general
we will not be too concerned about the object returned, we just will want to compare for equality so
that we can see if two objects have the same type as was done in the code above.
The following activity will let you see the other methods of the Object class. You will see that they
are interrelated to a certain extent.

Activity 6-1:
Read Section 11.7 of textbook (pp. 434–435).
1. In the example program above using the Person class, explain why the line:
Console.Writeline(p);
prints a line when p is not a string.
2. Suppose we implement the following code segment using the Person class above.
Person p1 = new Person(“Jo”);
Person p2 = new Person(“Jo”);
if (p1.Equals(p2))
Console.WriteLine(“p1 and p2 are equal”);
Explain why p1 and p2 are not equal (message is not displayed) even though they have
the same argument “Jo” passed to the constructor.

6-6
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

Examine the full list of member functions of the Object class in the Visual C# documentation:
http://msdn.microsoft.com/en-us/library/system.object_members.aspx
3. Explain in your own words how the GetHashCode() function is related to the
Equals() function.
4. Explain in your own words what the MemberwiseClone() member function does and
how you think it could be used.
5. Explain in your own words the relationship between Equals() and
ReferenceEquals() member functions.

Now that we have introduced the Object class we will use it to show how polymorphism works.

Polymorphism
In this section we will discuss the idea of polymorphism in object-oriented languages and how it
increases code re-use. We will do this by examining an example method that illustrates the concept.
Polymorphism is about using methods of a base class to process objects of a class derived from the
base class. Remember that objects of derived classes inherit methods and attributes, can redefine
them, but cannot remove them so they will always be present in objects of derived classes. Carefully
look at the following method definition.
public void PrintString(Object o)
{
Console.WriteLine(o);
}
This method will happily take a reference to any program object as an argument! C# allows variables
(a parameter variable in this case) declared with a particular class to hold references to any object of
derived classes. This makes sense because all objects of derived classes will also have inherited all of
the base class fields and members. Now, since the Object class is the base class of all class
hierarchies, this particular method will be able to take any object reference as a parameter. That in
turn implies that the WriteLine(o) function call will always be able to call the ToString()
member function inherited from the Object class for any object passed as parameter.
Suppose we have a simple class called Person defined as follows.
class Person
{
private readonly string name;

public Person(string n)
{
name = n;
}

public string GetName()


{
return name;
}
}
Now suppose we have the following code sequence which calls the PrintString() method as
follows.
Person p = new Person(“Barry”);
PrintString(p);
Then the following message would be printed on the console (assuming the Person class is declared
in the TestPoly name space).
TestPoly.Person
We have used the Object class to demonstrate polymorphism in C#. Although this is possible it is
not the major use of the concept. Re-use enhancements by polymorphism are much greater in larger
and more complex class hierarchies. We will demonstrate a simple case by deriving a class hierarchy
shown in Figure 6.1.

6-7
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

Person

-name: string

+Person(n: string)
+GetName(): string

Student StaffMember

-ID: int -room: string

+Student(n: string, id: int) +StaffMember(n: string, rm: string)

Figure 6.1 – extended class hierarchy


Now in the same way we used the Object class for polymorphic processing, we can use the Person
class to declare variables to process Student and StaffMember objects, and any other class derived
from the Person base class.
For example, we could declare the following method for printing the name stored in the name field for
object of class Person, Student or StaffMember.
public static void Printname(Person p)
{
Console.WriteLine(p.GetName());
}
Then we could print the names of various objects as follows.
Person p = new Person("Barry");
Student s1 = new Student("Jo", 1234);
StaffMember st1 = new StaffMember("Peter", "MG103");

Printname(p);
Printname(s1);
Printname(st1);
Notice how we have used one implementation of a method to print any of the three object types. This
is re-using the Printname() member function for several different objects, which happen to have a
common inheritance ancestor.
You may have already noticed that a potential problem exists here. What if the method called in the
base class is redefined in the derived class. For example, what if the Student class in the above
example redefined the GetName() method, which is perfectly legal in C# if the method is declared
virtual in the base class. We would then have a version of the method in both the base class and
the derived class. The answer is that C# performs dynamic binding. This means that the language
decides which method is appropriate at run-time so if the Student class did redefine the GetName()
method then its version of the GetName() method would be called.

Activity 6-2:
Read Section 12.1 to 12.3 in the Textbook (pp. 442-444). This reading also gives an
introduction to the rest of this topic. Try to answer the following:

1. In the example above, suppose we defined the above Printname() function as follows:

public static void Printname(Student s)


{
Console.WriteLine(s.GetName());
}
Is the call PrintName(p) legal when p is an instance of Person? Explain your answer.

6-8
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

2. Is the call PrintName(st) legal when st is an instance of StaffMember? Explain your


answer.

Next we see how the idea of polymorphism results in further extensions to the syntax of class
declaration, specifically abstract classes.

Abstract classes
The idea of polymorphism is so important to object-oriented program design that the language is
extended to allow better use of these facilities in large projects. In this section we will discuss abstract
classes which are classes designed to be base classes only, and never instantiated. In the next section
we will look at interfaces which take the idea one step further.
We can declare a class abstract which usually means that it contains abstract methods. A class with
abstract methods must be declared abstract. For example, suppose we have a class hierarchy for 2-
dimensionsal shapes. We will require each shape to return its area but we will not know in advanced
how each shape will describe itself. We can declare an abstract class Shape to make sure all derived
classes can return their area as follows.
abstract class Shape
{
private double x, y;
public Shape(double x, double y)
{
this.x = x;
this.y = y;
}

public abstract double Area();


}
Note that we have used the abstract keyword both on the class header and the abstract method it
contains. The abstract method is also different than we have seen before. Instead of a block delimited
by braces ({}) there is only a semicolon (;) signifying that there is no body defined here. A final
point to note is that abstract classes can have concrete methods and fields implemented and thus
inherited in derived classes. In this case we have some X and Y coordinates defined and initialised by
a constructor. These will be inherited and usable in any derived classes. We cannot, however, create
an object of this class.
Now we can define shapes and implement the Area() method for each of them. For example, we can
define a class called Square to represent square shapes as follows.
class Square : Shape
{
private double side;

public Square(double s) : base (0.0, 0.0)


{
side = s;
}

public override double Area()


{
return side*side;
}
}
Note that we must use the override keyword in the Area() function definition, even though it is
compulsory to define it here. We also initialise the X and Y coordinates to zero using the Shape
constructor to keep the example simple. Since Square is not an abstract class we can create objects
of the Square class.
We can now use polymorphism just like in the previous section to process objects derived from the
Shape class. For example the following is a function that will print out the area of any shape derived
from the Shape base class.
public static void PrintArea(Shape s)
{

6-9
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

Console.WriteLine("Area is {0}",s.Area());
}
There is a final aspect to abstract classes. We have not shown how properties may be declared
abstract. This is achieved in a similar way to methods. For example, we could add an abstract
property for colour (represented by an int type) to our Shape class with the following definition.
public abstract int colour
{
get;
set;
}
Notice how this definition again uses the semicolon in place of a block of code to perform the get and
set actions. As before, this means the property has a default implementation, however the abstract
keyword means that the body must be defined in all derived classes.
UML also has a special notation for abstract classes. The abstract class name is presented in italic
font, everything else appears similar to concrete classes. It is easy to miss the italics in many
diagrams so beware. Figure 6.2 shows the UML class diagram for the Shape/Square/Rectangle
relationship.

Shape
-x: double
-y: double

+Shape(x, y: double)
+Area(): double

Square Rectangle
-side: double -side1: double
-side2: double
+Square(s: double)
+Rectangle(s1: double, s2: double)

Figure 6.2 – Showing abstract classes in UML class diagrams


It may appear strange to you that we can have classes that cannot be used to create objects, why then
do we need them? There are two main reasons for this. One is that an abstract class can be used in a
polymorphic way for processing derived class instances when no actual instances are necessary. The
other is more from a design perspective, the abstract class designer can force users of the class to
implement certain methods so that future class derivations are forced to implement the methods
declared as abstract.

Activity 6-3:
Read Section 12.4 – 12.5.5 in the textbook (pp. 448 - 459). This reading contains a quite
complicated example, much closer to a real-life program than the simple examples we use in the
study guide.
1. Develop and test a definition for a class for a Rectangle shape (see Figure 6.2).
2. Develop and test code for using the above PrintArea() function to print the area for
a square object and a rectangle object. You will need to declare example objects
before calling the function.

Abstract classes are design to be used as base classes for other classes. What if we do not want a class
to be used as a base class? C# provides a keyword for this that we will examine next.

6-10
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

The sealed keyword


Using the abstract or virtual keywords in a method definition allows these methods to be
redefined in derived classes (using the override keyword). Further, once the override keyword is
used it implies the method may also be overridden in derived classes further down the inheritance
hierarchy. So what if a class designer would like to prevent a method from being overridden at lower
levels?
A class designer can declare a method to be unchangeable forever (enforced by the compiler) by using
the sealed keyword. For example, the following method can never be redefined in derived classes.
public sealed int GetID() { return ID; }
Note that a private method or a static method can never be redefined because they are already
sealed.
One further use of this keyword is when it is applied to a class definition. A class that is declared as
sealed can never be used as a base class. So for example, if we have the following header for the class
called Registration:
public sealed class Registration { … }
The compiler will cause an error if an attempt to use it as a base class is made.
The use of the sealed keyword is probably not as frequent as other class attributes. It does allow class
hierarchy designers to limit the redefinition of methods to their own hierarchy preventing redefinition
by other class users. Remember that a base class method must be declared as virtual or abstract
before redefinition is allowed at all. Also, in some cases a designer will not want a base class to be
extended as the class may model some concept which is desired to be forever bound to the particular
implementation. As an example, the standard string class in C# is sealed so that instances will
behave as per the .NET specification. This means the C# compiler and the .NET Framework Class
Library will behave as expected and not behave unexpectedly if string was extended by users to
perform functions differently.


Activity 6-4:

Read Section 12.6 in the Textbook (p. 466). Explain in your own words how the following
would be a valid declarations for a class designer:
public override sealed void DoSomething() {…}

The final section for this topic will look at interfaces which are a pure version of an abstract class that
has no implementation details at all.

Interfaces
An interface can be considered an abstract class with no methods implemented. It is not declared as a
class however, it is considered a separate structure called an interface. It also provides an additional
attribute useful for object-oriented design. A class can implement more than one interface. Although
it was not explicitly stated, a class can only be derived from one base class. However, a class can
implement as many interfaces as the programmer desires. This directly follows from the lack of
method implementation in an interface as there can be no confusion which method to execute at
runtime.
We will introduce interfaces with a very simple example. The following IPrintable interface is to
provide a single member function to print the object details.
public interface IPrintable
{
void Print(); // print object details
}

6-11
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

Notice the interface keyword is used where the class keyword appears in class definitions. You
should also take note that, since it is an interface, the keywords virtual or abstract are not
necessary in the method definitions since they are implied by the interface declaration. In addition,
individual methods cannot be declared public as this is an interfaces desired state and a syntax error
will be generated if you use an access specifier such as public.
To demonstrate use of this interface we will redefine the Student class from earlier in this topic. We
will extend the previous definition to implement the IPrintable interface as follows.
class Student : Person, IPrintable
{
// fields and methods as before

public void Print()


{
Console.WriteLine("Student name={0}, ID={1}", GetName(), ID);
}
}
Notice how we now have a base class and an interface in the definition header. We could add more
interfaces if we require them but we can only ever have one base class.
We can use interfaces for polymorphic processing in exactly the same way as previously. We can
declare variable of an interface type and then call member functions of the interface. For example, we
can use the above Student class definition to create an instance and then call the Print() member
as follows.
Student stud = new Student(“Jo”,12234);
IPrintable pr = stud;
pr.Print();
Notice how the variable pr is declared using the interface type. This code would call the Print()
method and produce the following output:
Student name=Jo, ID=12234
Interfaces also have a special notation in UML class diagrams. Figure 6.3 show the previous UML
diagram for the Person/Student/StaffMember relations extended to include the IPrintable
interface.

Person
<<interface>>
IPrintable -name: string

+Person(n: string)
+Print(): void +GetName(): string

Student StaffMember

-ID: int -room: string

+Student(n: string, id: int) +StaffMember(n: string, rm: string)

Figure 6.3 – Adding IPrintable interface to a class diagram


The dashed line is called realisation in UML and indicates interface implementation. In this example
diagram the StaffMember class and the Person class do not implement the IPrintable interface.
The following activity reading continues examples from the textbook. You should note that it is
common programming practice in C# to call interfaces with names starting with the letter ‘I’ as we
will do in this unit.

6-12
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces


Activity 6-5:
Read Section 12.7 in the Textbook (pp. 466-476). Note that this reading again uses a fairly
complex, though more real-world example. The actual code to do with interfaces forms a small
part of the overall example code.

1. What is the logical (non-syntax) difference between an abstract class and an interface?
Could you replace any interface with an abstract class?
2. Write down a definition of an interface for objects that can be incremented. It will need
one member function called Increment(). Then write down the definition of a class
that implements the interface. You will probably need some sort of number field in your
class.
3. Write down the definition of a function that would print an array of objects with the
IPrintable interface discussed above.

That will be enough class variations for designing very complex and complete class hierarchies. You
will come across all of these variations while using the .NET Framework Class Library, including
those you saw in the reading in Activity 6-5.

Topic Summary
This topic examined polymorphism and the C# support for it. We started by discussing the Object
class which is the root of all class inheritance hierarchies in C#. We used the Object class to show
how we can use polymorphism to write code to process all objects in a C# program. We then
demonstrated how we can use polymorphism to process our own class hierarchies, using a common
base class. Abstract classes were then introduced and we showed how these can be used for
polymorphic processing even though no objects can be instantiated using an abstract class. Finally,
we looked at interface structures which are purely abstract constructs in C#. Interfaces are a
description of method signatures which must be implemented in classes. Interfaces have the
additional attribute that a class may implement multiple interfaces, and so be processed by many
different code segments.

6-13
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

Answers to Activities
Activity 6-1:
1. The Person instance object p is automatically converted into a string by calling the ToString()
method of the object. Every object has one of these because it is a method inherited from the
Object class.
2. They are not equal because they are not the same object in memory. In this case the Equals()
method is inherited from the Object class and in that version of the method a check is made as to
whether the objects are the same object in memory. Note that the Equals() method could have
been redefined in the shown derived class Person to behave differently to the version inherited
from the Object class but was not in this case.
3. The GetHashCode() member function returns a unique identifier for each object in memory.
This means that two objects that return true with the Equals() function inherited from the
Object class will have the same hash code returned from the GetHashCode() function.
4. The MemberwiseClone() function copies the memory contents of one object to another. This
has a subtle effect when the object contains references to other objects. Supose we have the
following class definition:
class Person
{
private string name;
public Person(string n) { name = n; }
public string GetName() { return name;}
public Person GetCopy() { return (Person) MemberwiseClone(); }
}
And then suppose we execute the following code:
Person p1 = new Person("Barry");
Person p2 = p1.GetCopy();
The end result is that the two objects p1 and p2 end up with references to the same string object
as shown in the following diagram:
p1
"Barry"

p2

This is known as a shallow copy. A deep copy would have also created a new string object for
p2.
5. Both can compare if the two objects are the same two objects in memory. However, the
Equals() method may be overridden to have different behaviour, whereas the
ReferenceEquals() cannot so it always checks if the two object references given as
parameters are the same objects in memory.

Activity 6-2:
1. No. This is not permitted because polymorphism works with derived classes, not base classes.
The reason this cannot work is that the Student class may have methods and attributes that are
not defined in the Person base class, whereas in the reverse situation this is not possible.
2. No. Again, StaffMember is not derived from the Student class. Again, the reason this cannot
work is that the Student class may have methods and attributes that are not defined in the
StaffMember class. (StaffMember and Student are both derived from the same Person base
class).

6-14
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

Activity 6-3:
1. Here is a complete Rectangle class definition, including the constructor.
class Rectangle : Shape
{
private double side1;
private double side2;

public Rectangle(double s1, double s2) : base(0.0,0.0)


{
side1 = s1;
side2 = s2;
}

public override double Area()


{
return side1 * side2;
}
}
2. The following is an example test program for using squares and rectangles.
class TestAbstract
{
public static void PrintArea(Shape s)
{
Console.WriteLine("Area is {0}",s.Area());
}

public static void Main()


{
Square s = new Square(1.0);
Rectangle r = new Rectangle(0.7, 1.5);

PrintArea(s);
PrintArea(r);
}
}

Activity 6-4:
The reading briefly describes how this may be used. First, since we are using the override keyword
the method DoSomething() must be declared in a base class. Second, since we are now applying the
sealed keyword, in any derived classes this method may not be overridden. We are cutting off the
inheritance possibilities from this level of the inheritance hierarchy.

Activity 6-5:
1. There are two main differences between interfaces and abstract classes. The first is that an
interface cannot have any implementation for any of its methods. It is however also possible for
an abstract class to have no implementation for its methods. The second and distinct difference is
that we can inherit/implement more than one interface. A class can only ever use one class
(abstract or otherwise) as its base class.
So the answer to the second question is that you cannot replace an interface with an abstract class
because a derived class may want to use the interface and another class or interface to
respectively inherit or implement.
2. A possible interface definition is:
public interface IIncrementable
{
void Increment(); // increment the object
}
We can implement this interface in a class containing a number as in the following example:
public class Number implements IIncrementable

6-15
CSC10210 Study Guide Topic 6 -Polymorphism, abstract classes and interfaces

{
private int numb = 0;

public void Increment()


{
numb++;
}

public int GetNumb()


{
return numb;
}
}
Note that we have added the GetNumber() member function so you can use this class to test that
the number is really incremented.
3. This is an example function that will take an array of IPrintable objects (as defined in notes)
as an argument and print all of them.
public static void PrintAll(IPrintable [] ps)
{
foreach (IPrintable p in ps)
p.Print();
}
If you have not seen the foreach statement before (or forget how it works), the above definition
is equivalent to:
public static void PrintAll(IPrintable [] ps)
{
for (int i=0; i<ps.Length; i++)
ps[i].Print();
}

6-16

S-ar putea să vă placă și