下面先来叙述一下问题的描述:要求设计一个能描述所有图形公共属性和方法的的类(Shape),然后设计若干个不同的继承Shape的子类,比如三角形、圆形,矩形等等,基本功能要求:能提供面积和周长的计算和输出
接着我们就来用C#语言的封装和继承的特性分析一下这个类和子类的设计吧,首先就是父类的设计,想一想父类应该包含哪些属性,或者方法呢,对于每个图形我们知道他们都有自己的周长和面积,因此我们可以将其周长和面积放到父类Shape中即可,对于所有具体的某种图形都应该有计算和输出其周长和面积的方法,那计算和输出的方法应该放到子类中呢还是父类中去呢,我们可以从其变化的角度来分析,对于不同图形来说,它们周长和面积的计算公式是不一样的,因此我们可以这样设计,在父类中只是声明计算周长和面积的抽象方法,然后在其子类中去具体的实现即可,再来考虑输出周长和面积的方法应该放到哪儿呢,是子类还是父类呢?在此我是这样设计的,将输出周长和面积的方法在父类中设计成共有虚拟的(即用public virtual修饰的),这样做的好处是,子类的如果不去重写父类的输出方法,则默认调用父类的输出周长和面积的函数,若是子类对输出周长和面积的格式有一定的变化或者改进的话,子类就完全可以重写(overide)周长和面积输出方法,以满足子类的需要,当重写之后子类就会调用其重写后的方法,即使是转换成父类对象也是调用子类的重写的方法(这就是多态性的体现),好了分析就到这里吧;下面看看具体的代码实现:
在父类Shape代码实现如下:

1 /// <summary> 2 /// 所有图形形状的基类 3 /// </summary> 4 public abstract class Shape 5 { 6 private double _perimeter; 7 private double _area; 8 private string _shapename; 9 10 /// <summary> 11 /// 周长 12 /// </summary> 13 protected double Perimeter 14 { 15 get { return _perimeter; } 16 set { _perimeter = value;} 17 } 18 19 /// <summary> 20 /// 面积 21 /// </summary> 22 protected double Area 23 { 24 get { return _area; } 25 set { _area = value;} 26 } 27 28 /// <summary> 29 /// 形状的名称 30 /// </summary> 31 protected string ShapeName 32 { 33 get { return _shapename; } 34 set { _shapename = value; } 35 } 36 37 /// <summary> 38 /// 默认构造函数 39 /// </summary> 40 protected Shape() 41 { 42 _perimeter=0; 43 _area=0; 44 _shapename=""; 45 } 46 47 /// <summary> 48 /// 输出周长 49 /// </summary> 50 public virtual void PrintPerimeter() 51 { 52 Console.WriteLine(ShapeName + "的周长为:"+this.CalculatePerimeter()); 53 } 54 55 /// <summary> 56 /// 输出面积 57 /// </summary> 58 public virtual void PrintArea() 59 { 60 Console.WriteLine(ShapeName + "的面积为:"+this.CalculateArea()); 61 } 62 63 /// <summary> 64 /// 计算周长(因该方法为抽象方法,因此必须在子类中进行重写) 65 /// </summary> 66 /// <returns></returns> 67 public abstract double CalculatePerimeter(); 68 69 /// <summary> 70 /// 计算面积(因该方法为抽象方法,因此必须在子类中进行重写) 71 /// </summary> 72 /// <returns></returns> 73 public abstract double CalculateArea(); 74 }
注:Shape类设计成抽象的,是因为它应该是所有形状的基类;只能作为某种具体形状的父类,不能被实例化
下面简单设计了一下子类圆和三角形类的实现:
圆类代码实现如下:

1 /// <summary> 2 /// 圆形 3 /// </summary> 4 public class Circle : Shape 5 { 6 const double PI=3.1415; 7 private double _r = 0; 8 9 /// <summary> 10 /// 圆的半径 11 /// </summary> 12 public double R 13 { 14 get { return _r; } 15 set { _r = value; } 16 } 17 18 /// <summary> 19 /// 默认构造函数 20 /// </summary> 21 public Circle(): base() 22 { 23 this.ShapeName ="圆"; 24 } 25 26 public Circle(double r):this() 27 { 28 this.R = r; 29 } 30 31 /// <summary> 32 /// 设置半径 33 /// </summary> 34 /// <param name="r"></param> 35 public void SetRadius(double r) 36 { 37 this.R = r; 38 } 39 40 /// <summary> 41 /// 计算周长 42 /// </summary> 43 /// <returns></returns> 44 public override double CalculatePerimeter() 45 { 46 Perimeter = 2 * PI * R; 47 return Perimeter; 48 } 49 50 /// <summary> 51 /// 计算面积 52 /// </summary> 53 /// <returns></returns> 54 public override double CalculateArea() 55 { 56 Area = PI * R * R; 57 return Area; 58 } 59 60 /// <summary> 61 /// 重写父类虚拟的PrintArea方法输出面积 62 /// </summary> 63 public override void PrintArea() 64 { 65 Console.WriteLine("执行Circle.PrintArea()方法输出面积"); 66 base.PrintArea(); 67 } 68 69 /// <summary> 70 /// 重写父类虚拟的PrintPerimeter方法 输出周长 71 /// </summary> 72 public override void PrintPerimeter() 73 { 74 Console.WriteLine("执行Circle.PrintPerimeter()方法输出周长"); 75 base.PrintPerimeter(); 76 } 77 }
三角形类代码实现如下:

/// <summary> /// 三角形 /// </summary> public class Triangle:Shape { //私有字段 private double _a; private double _b; private double _c; #region 属性成员 public double A { get { return _a; } } public double B { get { return _b; } } public double C { get { return _c; } } #endregion /// <summary> /// 继承基类默认构造函数 /// </summary> public Triangle():base() { this.ShapeName="三角形"; } /// <summary> /// 构造函数(需传入三角形的三个边长) /// </summary> /// <param name="a">第一个边</param> /// <param name="b">第二个边</param> /// <param name="c">第三个边</param> public Triangle(double a, double b, double c):this() { if (this.CheckSide(a, b, c) == true) { this._a = a; this._b = b; this._c = c; } } /// <summary> /// 设置三个边的长度 /// </summary> /// <param name="a">第一个边</param> /// <param name="b">第二个边</param> /// <param name="c">第三个边</param> public void SetThreeSideLength(double a, double b, double c) { if (this.CheckSide(a, b, c) == true) { this._a = a; this._b = b; this._c = c; } } /// <summary> /// 检查边长 /// </summary> /// <param name="a">第一个边</param> /// <param name="b">第二个边</param> /// <param name="c">第三个边</param> /// <returns></returns> public bool CheckSide(double a, double b, double c) { if (a > 0 && b > 0 && c > 0) { if ((((a + b) > c && (a + c) > b && (b + c) > a) && (Math.Abs(a - b) < c && Math.Abs(a - c) < b && Math.Abs(b - c) < a))) { return true; } else { throw new ArgumentException("您输入的三个边长不能构成有效的三角形,请检查其边长!"); } } else { throw new ArgumentException("三个边长必须大于0!"); } } /// <summary> /// 计算周长 /// </summary> public override double CalculatePerimeter() { this.Perimeter = A + B + C; return Perimeter; } /// <summary> /// 计算面积 /// </summary> public override double CalculateArea() { //已知三个边面积计算公式:S△=√〔p(p-a)(p-b)(p-c)〕 〔p=1/2(a+b+c)〕(海伦—秦九韶公式) double P= (A + B + C) / 2; Area = Math.Sqrt(P * (P - A) * (P - B) * (P - C)); return Area; } }
测试调用代码:

1 //Shape triangle = new Triangle(3,4,5); 2 //triangle.PrintArea(); 3 //triangle.PrintPerimeter(); 4 5 //Shape circle = new Circle(10); 6 //circle.PrintArea(); 7 //circle.PrintPerimeter(); 8 //Console.ReadKey(); 9 Shape[] shapes = new Shape[] 10 { 11 new Circle(10), 12 new Circle(8), 13 new Circle(100), 14 new Triangle(3,4,5), 15 new Triangle(10,10,10), 16 new Triangle(8,10,12) 17 }; 18 foreach (Shape shape in shapes) 19 { 20 Console.WriteLine("***********************************"); 21 shape.PrintArea(); 22 shape.PrintPerimeter(); 23 Console.WriteLine("***********************************"); 24 Console.WriteLine(); 25 } 26 Console.ReadKey();
PS:本篇博客只是我学习的理解和认识,希望对于刚学C#语言的朋友们理解继承、封装和多态有一定的帮助作用,有不对之处请评论告知!