Skip to content

面向对象

面向对象编程(OOP, Object-Oriented Programming)是 C# 的核心思想之一, 它通过将数据和操作数据的行为封装在对象中,来实现代码的组织、复用和扩展。

OOP 使得程序设计更加直观、可维护,并且有助于创建模块化、可扩展的代码结构。

C# 中的 OOP 依赖于四个核心原则:

  1. 封装(Encapsulation)
  2. 继承(Inheritance)
  3. 多态性(Polymorphism)
  4. 抽象(Abstraction)

优势

  1. 代码复用:通过继承和封装,可以复用已有的代码,减少重复劳动。
  2. 易于维护:OOP 提供了良好的结构化设计,使得代码更易维护和扩展。
  3. 模块化:对象和类提供了模块化的方式来组织代码,使得复杂的程序更易管理。
  4. 灵活性和可扩展性:通过多态性,可以在不修改原有代码的情况下扩展程序功能。

封装(Encapsulation)

封装 是将数据(字段、属性)和操作数据的方法封装在对象中,并通过访问修饰符(如 public、private、protected)控制这些数据的可见性和访问权限。

封装的目的在于隐藏对象的内部实现,仅对外提供接口,确保对象的状态只能通过可控的方式修改。

示例:

C#
public class Person
{
    // 私有字段,只能通过属性访问
    private string name;
    private int age;

    // 公有属性,提供对字段的封装
    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int Age
    {
        get { return age; }
        set 
        {
            if (value >= 0)
                age = value;
        }
    }

    // 方法,操作数据
    public void DisplayInfo()
    {
        Console.WriteLine($"Name: {Name}, Age: {Age}");
    }
}

class Program
{
    static void Main()
    {
        Person person = new Person();
        person.Name = "Alice";
        person.Age = 25;
        person.DisplayInfo();  // 输出: Name: Alice, Age: 25
    }
}

通过封装,Person 类的内部实现细节对外部是隐藏的,外部代码只能通过属性来访问和修改 name 和 age 字段。

继承(Inheritance)

继承 是 OOP 的另一大特性,允许一个类从另一个类继承其属性和方法。

继承可以实现代码复用,并且通过扩展已有类的功能来创建新类。

C# 中,继承通过 : 符号实现,一个类可以从一个基类继承。

C#
// 基类
public class Animal
{
    public string Name { get; set; }

    public void Eat()
    {
        Console.WriteLine($"{Name} is eating.");
    }
}

// 派生类
public class Dog : Animal
{
    public void Bark()
    {
        Console.WriteLine($"{Name} is barking.");
    }
}

class Program
{
    static void Main()
    {
        Dog dog = new Dog();
        dog.Name = "Buddy";
        dog.Eat();   // 基类的方法
        dog.Bark();  // 派生类的方法
    }
}

在这个例子中,Dog 类继承自 Animal 类,Dog 类不仅拥有 Animal 类的 Eat 方法,还添加了自己独特的 Bark 方法。

多态性(Polymorphism)

多态性 允许使用统一的接口调用不同类型的对象,从而实现灵活的代码设计。在 C# 中,多态性主要通过方法重写(Method Overriding)和接口来实现。

  1. 方法重写:通过在派生类中重新定义基类中的虚方法(virtual)或抽象方法(abstract)。
  2. 接口实现:通过定义接口,类实现接口中的方法,进而通过接口进行调用。

示例:

C#
public class Animal
{
    // 虚方法,可以在派生类中被重写
    public virtual void MakeSound()
    {
        Console.WriteLine("Animal makes a sound.");
    }
}

public class Dog : Animal
{
    // 重写基类的虚方法
    public override void MakeSound()
    {
        Console.WriteLine("Dog barks.");
    }
}

public class Cat : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Cat meows.");
    }
}

class Program
{
    static void Main()
    {
        Animal myDog = new Dog();
        Animal myCat = new Cat();

        // 基于多态性,通过基类引用调用派生类的方法
        myDog.MakeSound();  // 输出: Dog barks.
        myCat.MakeSound();  // 输出: Cat meows.
    }
}

在这个例子中,虽然 myDog 和 myCat 都是 Animal 类型,但它们调用的是各自派生类的 MakeSound 方法。

更多参考类与接口

抽象(Abstraction)

抽象 是隐藏对象复杂实现细节,只对外暴露最必要的接口。C# 中的抽象通过 抽象类 和 接口 实现。

抽象类可以包含未实现的方法(抽象方法),具体实现由派生类提供;接口则是纯行为契约,规定了类必须实现的方法或属性。

示例:

C#
public abstract class Animal
{
    // 抽象方法,必须由派生类实现
    public abstract void MakeSound();

    // 非抽象方法,可以被派生类直接使用
    public void Sleep()
    {
        Console.WriteLine("Animal is sleeping.");
    }
}

public class Dog : Animal
{
    // 实现抽象方法
    public override void MakeSound()
    {
        Console.WriteLine("Dog barks.");
    }
}

class Program
{
    static void Main()
    {
        Animal myDog = new Dog();
        myDog.MakeSound();  // 输出: Dog barks.
        myDog.Sleep();      // 输出: Animal is sleeping.
    }
}

面向对象与面向切面(AOP, Aspect-Oriented Programming)

面向切面编程(AOP, Aspect-Oriented Programming)是与面向对象编程(OOP)相辅相成的一种编程范式,旨在通过分离关注点来增强代码的模块化。

面向切面编程关注的是横切关注点(Cross-Cutting Concerns),即那些在多个模块或类中重复出现的功能,比如日志记录、事务管理、权限控制等。

在 C# 中,虽然没有直接的语言内置支持 AOP,但通过使用一些设计模式和第三方库,可以实现面向切面编程。

面向切面编程的概念

  1. 切面(Aspect):切面代表横切关注点,它是独立于业务逻辑的模块,如日志、事务、缓存等。这些功能通常跨越多个类或模块。
  2. 连接点(Join Point):连接点是在程序执行过程中,可以插入切面代码的位置。在 C# 中,常见的连接点包括方法调用、异常抛出等。
  3. 切入点(Pointcut):切入点定义了在哪些连接点应用切面。通过切入点,可以指定哪些方法、类或模块需要执行某个横切关注点的逻辑。
  4. 通知(Advice):通知是具体要在切入点执行的代码,它描述了切面的行为。常见的通知类型有: 前置通知(Before Advice):在目标方法执行之前执行。

后置通知(After Advice):在目标方法执行之后执行。

异常通知(After Throwing Advice):在方法抛出异常时执行。

环绕通知(Around Advice):包围目标方法的执行,既可以在目标方法前,也可以在目标方法后执行。

  1. 织入(Weaving):织入是将切面代码插入到应用程序中的过程。在 C# 中,可以通过编译时、运行时或加载时织入切面。

常见框架:

如 PostSharp、AspectCore、Castle DynamicProxy 等

Document Base on VitePress.