/* This is the C# version of the shapes example demonstrating "real", as opposed to superficial, object oriented programming. It is nearly identical to the java version: you can look at the java version at https://cs.hofstra.edu/~cscccl/csc123/shapes2.java The only real difference to note is that instances variables in C# are private by default, and the presence of the keyword "override" in the implementation of the non-abstract area and circumference functions. The virtual/abstract - override behavior, which enables dynamic dispatch, is the default in java. Compile with csc on native windows, or with mcs with Mono (recommended). This gives you a shapes2.exe. run with (mono) shapes2.exe */ using System; abstract class shape { protected int x, y; public /*virtual*/ double distance(shape other) { int dx = x-other.x, dy = y-other.y; return Math.Sqrt(dx*dx + dy*dy); } public abstract double area(); //abstract methods are already "virtual" public abstract double circumference(); }//shape class circle : shape // inheritance is public by default { double radius; // this is private by default. public circle(int a, int b, int r) {x=a;y=b;radius=r;} public override double area() {return Math.PI*radius*radius;} public override double circumference() {return Math.PI*radius*2;} public double get_radius() { return radius; } }//circle class rectangle : shape { double width, height; public rectangle(int a, int b, double w, double h) { x=a; y=b; width=w; height=h; } public override double area() { return width*height; } public override double circumference() { return width*2 + height*2; } }//rectangle // ... triangle, etc public class shapes2 { public static void Main() { shape[] S = new shape[4]; S[0] = new circle(3,4,5); S[1] = new circle(50,60,10); S[2] = new rectangle(3,4,5,6); S[3] = new rectangle(30,40,50,60); double totalarea = 0; foreach(var c in S) totalarea+=c.area(); //dynamic dispatch Console.WriteLine("total area is "+totalarea); // Now for a weakness of OOP, in any langauge: COMPROMISE // ON STATIC TYPE SAFETY. shape s = new circle(3,4,5); //valid, a circle "is a" shape double r = ((circle)s).get_radius(); // type casting is required because the compiler only knows that // s is a shape, not a circle. But if this cast is allowed, then.. s = new rectangle(5,6,7,8); //s can point to a rectangle too r = ((circle)s).get_radius(); // THIS ALSO COMPILES. // It compiled for the same reason that the previous cast compiled. // In C# (and Java), this results in a runtime type casting error. // This kind of type casting is not just a compile-time notion, it // results in type-checking at runtime, with dynamic type information. // In C++, this kind of type casting is equivalent to // dynamic_cast(s). Otherwise, with just (circle*)s, // the type cast is only done at compile time. There will be no // runtime check for valid casting: you lose all measure of // type safety, static and dynamic and the program will interpret // whatever bits it finds in memory as the "radius" of a rectangle. }//main }