引言
面向对象编程(Object-Oriented Programming,OOP)是一种编程范式,它使用“对象”来设计软件。在C#中,面向对象的概念得到了全面的支持,这包括类、对象、继承、封装、多态等关键特性。
下面介绍面向对象的三大特性。
封装
将数据和操作数据的方法封装一起,形成一个类,隐藏内部的实现,只暴露接口给外部使用。C#中使用访问修饰符如public
、private
、protected
和internal
来限制对类成员的访问。因此封装提高代码复用性和可维护性,保护数据的安全性。
假设我们要求不同图形的面积。
设定一个图形基类,无需知道内部方法的实现,只需知道有一个图形基类。
// 图形基类
class Shape
{
public virtual double CalculateArea()//----->这里体现封装
{
return 0.0;
}
}
继承
一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性和方法,而子类可以重写或扩展基类的行为,同时保留基类的功能。继承的好处是实现代码的重用,同时也可以建立类之间的关系。
接着上面的图形基类,我们可以派生出圆和矩形等等图形的类。每个图形的求面积方法不同,因此需要重写求面积的方法。
// 派生类1 - 圆形
class Circle : Shape//----->这里体现继承
{
public double Radius { get; set; }//定义Radius属性用于代表圆的半径
public Circle(double radius)//定义一个有参构造函数,用于圆的初始化
{
Radius = radius;
}
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}
// 派生类2 - 矩形
class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double width, double height)
{
Width = width;
Height = height;
}
public override double CalculateArea()
{
return Width * Height;
}
}
基类会有很多属性和方法 ,继承基类的子类同样也会有,就不需在定义类使写那么多属性和方法。比如定义一个敌人Enemy类,它有血量,攻击力,攻击范围,移速等等属性,有造成伤害,死亡,追击玩家,在攻击范围对玩家造成伤害的方法等等。假设我们要新建一个史莱姆敌人,作为敌人可以对玩家造成伤害,追击玩家等等,我们便可以继承Enemy类,再添加史莱姆的特点等等(比如史莱姆死亡并不会马上死亡,而是会分成两个史莱姆)。使用继承的好处是方便管理和新加内容,当基类的内容并不多时也可以不用继承的形式。
多态
指不同类型对象对同一种方法可以有不同的响应,可以提高代码的可扩展性和可维护性。多态分为动态多态(重写(override))和静态多态(重载(overload))。
动态多态-重写
重写是重新定义子类继承父类已有的虚方法。
在上面的例子中,子类对基类的方法虚方法进行修改,其实就是重写(Override关键字)。我们定义了圆和矩形类并继承了图形基类,在Main中进行初始化和调用求面积方法。
static void Main()
{
Shape[] shapes = new Shape[3];
shapes[0] = new Circle(5.0);
shapes[1] = new Rectangle(4.0, 6.0);
shapes[2] = new Circle(3.0);
foreach (Shape shape in shapes)
{
----->这里体现多态
double area = shape.CalculateArea(); // 多态性:根据对象的实际类型调用相应的方法
Console.WriteLine($"面积为 {area}");
}
}
在初始化圆和矩形赋予了半径和边长,调用求面积方法即可求出。
静态多态-重载
重载是指同一类中的同名方法有不同的参数列表。
简单来说是同一个方法可以输入不同的参数数量(前提是该方法有对应的虚方法)。
我们重新定义Shape基类,对求面积的方法内的不同参数进行不同的输出。
class Shape
{
// 这里我们定义一个重载的方法CalculateArea,但暂时不实现具体逻辑,
// 因为具体的计算逻辑将由派生类实现。
public virtual double CalculateArea(double width, double height) => 0.0;//简写,内容同上的CalculateArea方法
public virtual double CalculateArea(double radius) => 0.0;
}
再对矩形和圆进行修改,属性和构造函数不变,修改的是求面积的方法。
class Circle : Shape
{
public double Radius { get; set; }
public Circle(double radius)
{
Radius = radius;
}
// 重写Shape类中与Circle相关的CalculateArea方法
public override double CalculateArea(double radius)
{
return Math.PI * radius * radius;
}
}
class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double width, double height)
{
Width = width;
Height = height;
}
public override double CalculateArea(double width, double height)
{
return width * height;
}
}
在Main中便可以输入不同的参数数量得出不同的求面积方法。
static void Main()
{
Rectangle rectangle = new Rectangle(4, 5);//两个参数代表的是矩形的长宽
Circle circle = new Circle(3);//一个参数代表的是圆的半径
Console.WriteLine($"矩形面积为 {rectangle.CalculateArea(rectangle.Width, rectangle.Height)}");
Console.WriteLine($"圆面积为 {circle.CalculateArea(circle.Radius)}");
}
总结
通过例子对比重写和重载对图形的定义和初始化,不难看出,重载的方法虽然代码直观,但需要注意参数的数量,一不小心会搞错图形,且会存在重复性,比如求正方形只需要边长一个参数,而求圆也是只需要一个参数。
通过以上的例子希望能帮助您更好的理解OOP的三大特征,感谢观看。