360 likes | 448 Views
Introduction. Polymorphism allows programmers to develop: Programs that handle a wide variety of related classes in a generic manner Systems that are easily extensible and maintainable. Derived-Class-Object to Base-Class-Object Conversion. Class hierarchies
E N D
Introduction Polymorphism allows programmers to develop: • Programs that handle a wide variety of related classes in a generic manner • Systems that are easily extensible and maintainable
Derived-Class-Object to Base-Class-Object Conversion • Class hierarchies • Can assign derived-class objects to base-class references (eg. circle = cylinder;) • Can explicitly cast between types in a class hierarchy (eg. cylinder = (Cylinder) circle;) as long as circle references a cylinder object or an object of a derived class of cylinder (eg. pipe object) • An object of a derived-class can be treated as an object of its base-class • Good use: Array of base-class references that refer to objects of many derived-class types • Base-class object is NOT an object of any of its derived classes
Create a Point object Assign a Point reference to reference a Circle object Create a Circle object Use base-class reference to access a derived-class object Assign a Circle reference to reference a Point object (downcast) Use derived-class reference to access a base-class object Point point1 = new Point( 30, 50 ); Circle circle1 = new Circle( 120, 89, 2.7 ); string output = "Point point1: " + point1.ToString() + "\nCircle circle1: " + circle1.ToString(); // use 'is a' relationship to assign // Circle circle1 to Point reference Point point2 = circle1; output += "\n\nCCircle circle1 (via point2): " + point2.ToString(); // downcast (cast base-class reference to derived-class //data object type) point2 to Circle circle2 Circle circle2 = ( Circle ) point2; output += "\n\nCircle circle1 (via circle2): " + circle2.ToString(); output += "\nArea of circle1 (via circle2): " + circle2.Area().ToString( "F" );
Test if point1 references a Circle object – it does not // attempt to assign point1 object to Circle reference if ( point1 is Circle ) { circle2 = ( Circle ) point1; output += "\n\ncast successful"; } else { output += "\n\npoint1 does not refer to a Circle"; } MessageBox.Show( output, "Demonstrating the 'is a' relationship" );
Comments • With virtual method, such as ToString – the version called depends on type of object, not type of reference. • Assigning a base-class object (or a base-class reference) to a derived-class reference (without an explicit cast) is a syntax error. • If an invalid cast is attempted, C# throws an InvalidCastException • The is keyword enables a program to determine whether a cast operation would be successful.
Types and switch Statements • Using switch to determine the type of an object • Distinguish between object, perform appropriate action depending on object type • Potential problems using switch • Forgetting to include a type test or testing all possible cases • Forgetting to modify all relevant switch structures, when new types are added • Every addition or deletion of a class requires modification of every switch statement in the system; • C#’s polymorphic mechanism performs the equivalent logic, eliminating unnecessary switch logic.
Polymorphism Examples • SpaceObject base-class – contains method DrawYourself • Martian derived-class (implements DrawYourself) • Venutian derived-class (implements DrawYourself) • Plutonian derived-class (implements DrawYourself) • SpaceShip derived-class (implements DrawYourself) • A screen-manager program may contain a SpaceObject array of references to objects of various classes that derive from SpaceObject • To refresh the screen, the screen-manager calls DrawYourself on each object in the array • The program polymorphically calls the appropriate version of DrawYourself on each object, based on the type of that object
Polymorphism Example SpaceObject [] spaceobjects = new SpaceObject [10]; //use switch to construct each object… switch (inputCH ) { case ‘V’: spaceobjects[i] = new Venuetian( ); break; . : } //now process them – no need for switch foreach (s in spaceobjects) s.DrawYourself();
Abstract Classes and Methods • Abstract classes • Cannot be instantiated • Used as base classes • Class definitions are not complete – derived classes must define the missing pieces • Can contain abstract methods and/or abstract properties • Have no implementation • Derived classes must override inherited abstract methods and properties to enable instantiation
Abstract Classes and Methods • Abstract classes are used to provide appropriate base classes from which other (concrete) classes may inherit • Abstract base classes are too generic to define real objects • To define an abstract class, use keyword abstract in the declaration • To declare a method or property abstract, use keyword abstract in the declaration; abstract methods and properties have no implementation
Abstract Classes and Methods • Any class with an abstract method or property must be declared abstract • Concrete classes use the keyword override to provide implementations for all the abstract methods and properties of the base-class • Even though abstract classes cannot be instantiated, we can use abstract class references to refer to instances of any concrete class derived from the abstract class
Case Study: Inheriting Interface and Implementation • Abstract base class Shape • Concrete virtual method Area (default return value is 0) • Concrete virtual method Volume (default return value is 0) • Abstract read-only property Name • Class Point2 inherits from Shape • Overrides property Name (required) • Does NOT override methods Area and Volume • Class Circle2 inherits from Point2 • Overrides property Name • Overrides method Area, but not Volume • Class Cylinder2 inherits from Circle2 • Overrides property Name • Overrides methods Area and Volume
Declaration of virtual methods Area and Volume with default implementations Declaration of abstract class Shape Declaration of read-only abstract property Name; implementing classes will have to provide an implementation for this property publicabstractclass Shape { // return Shape's area publicvirtualdouble Area() { return0; } // return Shape's volume publicvirtualdouble Volume() { return0; } // return Shape's name publicabstractstring Name { get; } }
Point2’s implementation of the read-only Name property Class Point2 inherits from class Shape // Point2 inherits from abstract class Shape publicclass Point2 : Shape { privateint x, y; // Point2 coordinates // implement abstract property Name of class Shape publicoverridestring Name { get { return"Point2"; } } Point2.cs
Override the Area method (defined in class Shape) Override the read-only Name property Definition of class Circle2 which inherits from class Point2 // Circle2 inherits from class Point2 publicclass Circle2 : Point2 { privatedouble radius; // Circle2 radius // calculate Circle2 area publicoverridedouble Area() { returnMath.PI * Math.Pow( Radius, 2 ); } // override property Name from class Point2 publicoverridestring Name { get { return"Circle2"; } }
Class Cylinder2 derives from Circle2 // Cylinder2 inherits from class Circle2 publicclass Cylinder2 : Circle2 { privatedouble height; // Cylinder2 height // default constructor public Cylinder2() { // implicit call to Circle2 constructor occurs here } // constructor public Cylinder2( intxValue, intyValue, doubleradiusValue, doubleheightValue ) : base( xValue, yValue, radiusValue ) { Height = heightValue; } // property Height publicdouble Height { get { return height; } set {// ensure non-negative height value if ( value >= 0 ) height = value; } } Cylinder2.cs
Override read-only property Name Override Area implementation of class Circle2 Override Volume implementation of class Shape // calculate Cylinder2 area publicoverridedouble Area() { return2 * base.Area() + base.Circumference() * Height; } // calculate Cylinder2 volume publicoverridedouble Volume() { returnbase.Area() * Height; } // return string representation of Circle2 object publicoverridestring ToString() { returnbase.ToString() + "; Height = " + Height; } // override property Name from class Circle2 publicoverridestring Name { get { return"Cylinder2"; } } } // end class Cylinder2 Cylinder2.cs
Create an array of Shape objects Assign a Shape reference to reference a Point2 object Assign a Shape reference to reference a Circle2 object Assign a Shape reference to reference a Cylinder2 object // Demonstrates polymorphism in Point-Circle-Cylinder hierarchy. publicpartialclassAbstractShapesForm : Form { public AbstractShapesForm() { InitializeComponent(); // instantiate Point2, Circle2 and Cylinder2 objects Point2 point = new Point2( 7, 11 ); Circle2 circle = new Circle2( 22, 8, 3.5 ); Cylinder2 cylinder = new Cylinder2( 10, 10, 3.3, 10 ); // create empty array of Shape base-class references Shape[] arrayOfShapes = new Shape[ 3 ]; // arrayOfShapes[ 0 ] refers to Point2 object arrayOfShapes[ 0 ] = point; // arrayOfShapes[ 1 ] refers to Circle2 object arrayOfShapes[ 1 ] = circle; // arrayOfShapes[ 1 ] refers to Cylinder2 object arrayOfShapes[ 2 ] = cylinder; string output = point.Name + ": " + point + "\n" + circle.Name + ": " + circle + "\n" + cylinder.Name + ": " + cylinder;
Use a foreach loop to access every element of the array // display Name, Area and Volume for each object // in arrayOfShapes polymorphically foreach( Shape shape in arrayOfShapes ) { output += "\n\n" + shape.Name + ": " + shape + "\nArea = " + shape.Area().ToString( "F" ) + "\nVolume = " + shape.Volume().ToString( "F" ); } outputLabel.Text = output; } } Rely on polymorphism to call appropriate version of methods
sealed Classes and Methods • sealed is a keyword in C# • sealed methods cannot be overridden in a derived class • Methods that are declared static and private, are implicitly sealed • sealed classes cannot have any derived-classes • Creating sealed classes can allow some runtime optimizations • e.g., virtual method calls can be transformed into non-virtual method calls
Case Study: Creating and Using Interfaces • Interfaces specify the public services (methods and properties) that classes must implement • Interfaces provide no default implementations (i.e., no instance variables and no default-method implementations) vs. abstract classes which may provide some default implementations • Interfaces are used to “bring together” or relate disparate objects that relate to one another only through the interface
Case Study: Creating and Using Interfaces • Interfaces are defined using keyword interface • Use inheritance notation to specify a class implements an interface (ClassName : InterfaceName) • Classes may implement more then one interface (a comma separated list of interfaces) • Classes that implement an interface must provide implementations for every method and property in the interface definition • Example: interface IShape that specifies methods Area and Volume and property Name. • Example: interface IAge that returns information about an object’s age • Can be used by classes for people, cars, trees (all have an age)
Definition of interface IAge Classes implementing this interface will have to define read-only properties Age and Name // Interface IAge declares property for setting and getting age. publicinterface IAge { int Age { get; } string Name { get; } }
Class Person implements the IAge interface Definition of Age property (required) publicclass Person : IAge { privatestring firstName; privatestring lastName; privateint yearBorn; // constructor public Person( string firstNameValue, string lastNameValue, int yearBornValue ) { firstName = firstNameValue; lastName = lastNameValue; if ( yearBornValue > 0 && yearBornValue <= DateTime.Now.Year ) yearBorn = yearBornValue; else yearBorn = DateTime.Now.Year; } // property Age implementation of interface IAge publicint Age { get { return DateTime.Now.Year - yearBorn; } }
Definition of Name property (required) // property Name implementation of interface IAge publicstring Name { get { return firstName + " " + lastName; }
Implementation of Age property ( required) Class Tree implements the IAge interface // Class Tree contains number of rings corresponding to its age. publicclass Tree : IAge { privateint rings; // number of rings in tree trunk // constructor public Tree( int yearPlanted ) { // count number of rings in Tree rings = DateTime.Now.Year - yearPlanted; } // increment rings publicvoid AddRing() { rings++; } // property Age implementation of interface IAge publicint Age { get { return rings; } }
Definition of Name property (required) // property Name implementation of interface IAge publicstring Name { get { return"Tree"; } } } // end class Tree Tree.cs
Create array of IAge references Assign an IAge reference to reference a Tree object Assign an IAge reference to reference a Person object publicclass InterfacesTest { publicstaticvoid Main( string[] args ) { Tree tree = new Tree( 1978 ); Person person = new Person( "Bob", "Jones", 1971 ); // create array of IAge references IAge[] iAgeArray = new IAge[ 2 ]; // iAgeArray[ 0 ] refers to Tree object polymorphically iAgeArray[ 0 ] = tree; // iAgeArray[ 1 ] refers to Person object polymorphically iAgeArray[ 1 ] = person; // display tree information string output = tree + ": " + tree.Name + "\nAge is " + tree.Age + "\n\n"; // display person information output += person + ": " + person.Name + "\nAge is: " + person.Age + "\n\n"; InterfacesTest.cs
Use polymorphism to call the property of the appropriate class Use foreach loop to access each element of the array // display name and age for each IAge object in iAgeArray foreach ( IAge ageReference in iAgeArray ) { output += ageReference.Name + ": Age is " + ageReference.Age + "\n"; } MessageBox.Show( output, "Demonstrating Polymorphism" ); } // end method Main } // end class InterfacesTest
Delegates • Sometimes useful to pass methods as arguments to other methods • C# does not allow passing of method references directly as arguments, but does provide delegates • Delegates are sets of references to methods • Delegate objects can be passed to methods; methods can then invoke the methods the delegate objects refer to • Delegates that contain one method are known as singlecast delegates and are created or derived from class Delegate • Delegates that contain multiple methods are known as multicast delegates and are created or derived from class MulticastDelegate
Operator Overloading • C# contains many operators (such as + and - ) that are defined for some primitive types • It is often useful to use operators with user-defined types (e.g., a complex number class) • Operator notation may often be more intuitive then method calls • As in C++, C# allows programmers to overload operators to make them sensitive to the context in which they are used
Operator Overloading • Methods define the actions to be taken for the overloaded operator • They are in the form: publicstaticReturnTypeoperatoroperator-to-be-overloaded( arguments ) • These methods must be declared public and static • The return type is the type returned as the result of evaluating the operation • The keyword operator follows the return type to specify that this method defines an operator overload • The last piece of information is the operator to be overloaded (such as +, -, *, etc.) • If the operator is unary, one argument must be specified, if the operator is binary, then two, etc.
Overload the addition (+) operator for ComplexNumbers. Class ComplexNumber definition Method Add – provides an alternative to the addition operator publicclass ComplexNumber { privateint real; privateint imaginary; // overload the addition operator publicstatic ComplexNumber operator + ( ComplexNumber x, ComplexNumber y ) { returnnew ComplexNumber( x.Real + y.Real, x.Imaginary + y.Imaginary ); } // provide alternative to overloaded + operatorfor addition publicstatic ComplexNumber Add( ComplexNumber x, ComplexNumber y ) { return x + y; }
Overload the subtraction (-) operator for ComplexNumbers Method Subtract – provides an alternative to the subtraction operator Overloads the multiplication (*) operator for ComplexNumbers // overload the subtraction operator publicstatic ComplexNumber operator - ( ComplexNumber x, ComplexNumber y ) { returnnew ComplexNumber( x.Real - y.Real, x.Imaginary - y.Imaginary ); } // provide alternative to overloaded - operator // for subtraction publicstatic ComplexNumber Subtract( ComplexNumber x, ComplexNumber y ) { return x - y; } // overload the multiplication operator publicstatic ComplexNumber operator * ( ComplexNumber x, ComplexNumber y ) { returnnew ComplexNumber( x.Real * y.Real - x.Imaginary * y.Imaginary, x.Real * y.Imaginary + y.Real * x.Imaginary ); } // provide alternative to overloaded * operator // for multiplication publicstatic ComplexNumber Multiply( ComplexNumber x, ComplexNumber y ) { return x * y; } ComplexNumber.cs
Use addition operator to add two ComplexNumbers Use subtraction operator to subtract two ComplexNumbers Use multiplication operator to multiply two ComplexNumbers publicclass ComplexTest : Form { private ComplexNumber x = new ComplexNumber(); private ComplexNumber y = new ComplexNumber(); // add complex numbers privatevoid addButton_Click( object sender, System.EventArgs e ) { statusLabel.Text = x + " + " + y + " = " + ( x + y ); } // subtract complex numbers privatevoid subtractButton_Click( object sender, System.EventArgs e ) { statusLabel.Text = x + " - " + y + " = " + ( x - y ); } // multiply complex numbers privatevoid multiplyButton_Click( object sender, System.EventArgs e ) { statusLabel.Text = x + " * " + y + " = " + ( x * y ); }