Bootstrap

面向对象编程[OOP]

>面向对象编程是一种编程范式,它使用“对象”来表示数据和方法,以及对象之间的交互。面向对象编程的三大特征是继承、封装和多态,这些特性共同定义了面向对象编程的核心概念。

一、封装

1、含义

封装(Encapsulation)是一种面向对象编程(OOP)的重要概念,它指的是将数据和操作数据的函数绑定在一起,形成一个整体,也就是对象。封装的主要目的是隐藏对象的内部实现细节,只通过对象提供的公共接口与外界交互,从而提高程序的安全性和易维护性。

2、作用

  • 保护对象内部状态:封装可以保护对象内部的数据和状态,防止外部代码直接访问和修改它们。这有助于确保对象的完整性和一致性。
  • 提供简单的公共接口:通过封装,类可以提供一个简单的公共接口来访问其内部数据和行为。这使得使用者无需了解类的内部实现细节,降低了代码的复杂性和耦合性。

3、实现

  • 访问修饰符:使用访问修饰符(如 public、private、protected 和 internal)来控制类成员的可见性。通过将内部数据成员标记为 private,可以防止外部代码直接访问和修改数据,而只能通过公共方法(通常称为 getter 和 setter 方法)来间接访问和修改数据。
  • 属性:使用属性(Property)来封装数据成员,将数据成员和访问数据的公共方法结合在一起,形成一个统一的接口。属性可以隐藏数据成员的实际实现细节,并在需要时对数据进行验证和处理。
  • 方法:使用方法来封装特定的功能或行为。方法可以将复杂的操作封装在一个单独的函数中,从而简化代码的复杂性并提高代码的可重用性。

4、好处

  • 安全性:通过封装,我们可以控制对对象的访问,从而防止未经授权的访问和可能的错误。
  • 简化编程:封装允许我们将复杂的逻辑隐藏在简单的接口后面,使得使用这些逻辑变得更加简单。
  • 松耦合:封装使得类的实现细节可以被隐藏,这意味着类可以更容易地修改或替换,而不会影响到其他使用它的代码。

5、示例

以下是一个简单的C#封装示例:

public class Student  
{  
    // 私有数据成员  
    private string name;  
    private int age;  
  
    // 公共方法 (getter 和 setter)  
    public string Name  
    {  
        get { return name; }  
        set { name = value; }  
    }  
  
    public int Age  
    {  
        get { return age; }  
        set { age = value; }  
    }  
  
    // 公共方法 ( 例如: 获取年龄大于18的学生姓名 )  
    public string GetNameOfStudentsOlderThan18()  
    {  
        // 这里写逻辑代码  
        return null;  
    }  
}

在这个例子中,Student类的内部状态(name和age)被隐藏起来,只通过公共属性(Name和Age)来访问和修改。这样做的好处是,我们可以在不影响其他代码的情况下,改变Student类的内部实现细节。例如,我们可以在GetNameOfStudentsOlderThan18方法中修改获取学生姓名的逻辑,而不需要修改调用该方法的代码。
此外,封装还可以减少代码之间的耦合性,使代码更容易理解与维护。例如,如果其他代码需要获取年龄大于18的学生的姓名,它只需要调用GetNameOfStudentsOlderThan18方法,而不需要直接访问Student类的内部状态。这样做可以使代码更加模块化和可维护。

二、继承

1、含义

继承(Inheritance)是一种面向对象编程(OOP)的重要概念,它允许一个类(称为子类或派生类)从另一个类(称为父类或基类)中继承其成员(包括属性、方法和事件等)。子类可以重写父类中的方法或添加新的方法,同时还可以继承父类中的访问修饰符、访问修饰符和返回类型等
继承是一种机制,通过它,一个类(派生类或子类)可以继承另一个类(基类或父类)的字段、方法、属性和事件。这意味着,如果有一个现有的类,你可以创建一个新类,以现有的类为基础,添加或修改其行为。

2、作用

  • 代码重用: 继承允许我们重用基类的代码,而无需在新类中重新编写。
  • 扩展性: 如果基类不能完全满足需求,我们可以通过在派生类中添加新的字段、方法、属性或事件来扩展其功能。
  • 多态性: 当一个派生类的对象被当作一个基类对象对待时,就会发生多态。这允许我们编写可以处理基类的代码,而该代码实际上也可以处理派生类。
  • 层次结构:通过继承,我们可以创建一个类层次结构,这有助于我们组织和管理代码。例如,我们可以创建一个“动物”基类,然后创建“狗”和“猫”等派生类。
  • 封装:尽管这不仅限于继承,但继承确实为封装提供了强大的工具。基类可以封装内部实现,派生类只需要知道基类提供的公共接口即可。这样可以使代码更加模块化,易于理解和维护。

3、示例

在C#中,使用:运算符来表示继承。下面是一个简单的例子:

// 基类  
public class Animal   
{  
    public string Name { get; set; }  
    public void Eat()   
    {  
        Console.WriteLine(Name + " is eating.");  
    }  
}  
  
// 派生类  
public class Dog : Animal  // 这里表示Dog类继承了Animal类  
{  
    public void Bark()   
    {  
        Console.WriteLine(Name + " is barking.");  
    }  
}

在这个例子中,Dog 类继承了 Animal 类。因此,Dog 类具有 Animal 类中所有的成员(包括 Name 属性和 Eat() 方法),并且还可以添加自己特有的方法(例如 Bark() 方法)。我们可以创建一个 Dog 对象并调用其 Eat() 方法和 Bark() 方法,如下所示:

Dog myDog = new Dog();  
myDog.Name = "Rufus";  
myDog.Eat(); // 输出 "Rufus is eating."  
myDog.Bark(); // 输出 "Rufus is barking."

三、多态

在 C# 中,多态(polymorphism)是指一种对象可以有多种形态。也就是说同一个接口或基类的不同实例可以以不同的方式响应相同的消息或方法调用。在面向对象编程中,多态是通过继承和方法重写来实现的。

C# 中的多态有两种主要形式:编译时多态和运行时多态。

1、编译时多态(Compile-time Polymorphism)

编译时多态性:是指在编译时就可以确定调用的方法或属性,其机制是通过模板(泛型)和重载(Overloading)来实现的。这两种方法都是在编译时确定调用的方法。

1.1 模板(泛型):

示例:

public class MyGenericClass<T>  
{  
    public T GetItem()  
    {  
        // do something to get item  
    }  
}

1.2 方法重载(Overloading)

方法重载是在同一个类中定义多个同名方法,但它们具有不同的参数列表(类型、数量或顺序)。在编译时,编译器会根据调用方法时提供的参数来选择合适的方法。
示例:

public class MyClass  
{  
    public void MyMethod(string s)  
    {  
        // do something with string  
    }  
  
    public void MyMethod(int i)  
    {  
        // do something with int  
    }  
}

2、运行时多态(Run-time Polymorphism)

运行时多态性:是指在运行时才能确定调用的方法或属性,其机制是方法重写(Overriding)和接口实现(Interface Implementation。这两种形式的多态是在运行时确定调用的方法。

2.1 方法重写(Overriding)

方法重写是在派生类中重新定义基类中的虚方法。在运行时,根据对象的实际类型来选择调用哪个方法。(即通过继承和虚方法(virtual methods)来实现的)
示例1:

public class MyBaseClass  
{  
    public virtual void MyMethod()  
    {  
        // do something in base class  
    }  
}  
  
public class MyDerivedClass : MyBaseClass  
{  
    public override void MyMethod()  
    {  
        // do something in derived class  
    }  
}

在这个例子中,当你有一个 MyBaseClass 的引用,但实际上引用的是一个 MyDerivedClass 的实例时,调用 MyMethod 会执行 MyDerivedClass 中的实现,这就是运行时多态。

示例2:

using System;  
  
class Animal  
{  
    public virtual void MakeSound()  
    {  
        Console.WriteLine("The animal makes a sound");  
    }  
}  
  
class Dog : Animal  
{  
    public override void MakeSound()  
    {  
        Console.WriteLine("The dog barks");  
    }  
}  
  
class Cat : Animal  
{  
    public override void MakeSound()  
    {  
        Console.WriteLine("The cat meows");  
    }  
}  
  
class Program  
{  
    static void Main(string[] args)  
    {  
        Animal animal1 = new Dog();  
        Animal animal2 = new Cat();  
        animal1.MakeSound(); // 输出 "The dog barks"  
        animal2.MakeSound(); // 输出 "The cat meows"  
    }  
}

在上面的示例代码中,Animal 是基类,Dog 和 Cat 是子类,它们都重写了父类的 MakeSound 方法。在 Main 方法中,我们创建了两个 Animal 类型的引用 animal1 和 animal2,分别指向 Dog 和 Cat 对象。当我们调用它们的 MakeSound 方法时,会根据实际类型输出不同的结果,这就是多态的实现。

2.2 接口实现(Interface Implementation)

接口定义了一个或多个方法的签名,但没有实现。类可以实现一个接口,并提供这些方法的具体实现。多个类可以实现同一个接口,并以不同的方式实现这些方法,从而实现多态。

public interface IShape  
{  
    double Area();  
}  
  
public class Circle : IShape  
{  
    private double radius;  
    public Circle(double radius)  
    {  
        this.radius = radius;  
    }  
    public double Area()  
    {  
        return Math.PI * radius * radius;  
    }  
}  
  
public class Rectangle : IShape  
{  
    private double width, height;  
    public Rectangle(double width, double height)  
    {  
        this.width = width;  
        this.height = height;  
    }  
    public double Area()  
    {  
        return width * height;
    }
;