面向对象
面向对象编程(OOP, Object-Oriented Programming)是 C# 的核心思想之一, 它通过将数据和操作数据的行为封装在对象中,来实现代码的组织、复用和扩展。
OOP 使得程序设计更加直观、可维护,并且有助于创建模块化、可扩展的代码结构。
C# 中的 OOP 依赖于四个核心原则:
- 封装(Encapsulation)
- 继承(Inheritance)
- 多态性(Polymorphism)
- 抽象(Abstraction)
优势
- 代码复用:通过继承和封装,可以复用已有的代码,减少重复劳动。
- 易于维护:OOP 提供了良好的结构化设计,使得代码更易维护和扩展。
- 模块化:对象和类提供了模块化的方式来组织代码,使得复杂的程序更易管理。
- 灵活性和可扩展性:通过多态性,可以在不修改原有代码的情况下扩展程序功能。
封装(Encapsulation)
封装 是将数据(字段、属性)和操作数据的方法封装在对象中,并通过访问修饰符(如 public、private、protected)控制这些数据的可见性和访问权限。
封装的目的在于隐藏对象的内部实现,仅对外提供接口,确保对象的状态只能通过可控的方式修改。
示例:
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# 中,继承通过 : 符号实现,一个类可以从一个基类继承。
// 基类
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)和接口来实现。
- 方法重写:通过在派生类中重新定义基类中的虚方法(virtual)或抽象方法(abstract)。
- 接口实现:通过定义接口,类实现接口中的方法,进而通过接口进行调用。
示例:
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# 中的抽象通过 抽象类 和 接口 实现。
抽象类可以包含未实现的方法(抽象方法),具体实现由派生类提供;接口则是纯行为契约,规定了类必须实现的方法或属性。
示例:
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,但通过使用一些设计模式和第三方库,可以实现面向切面编程。
面向切面编程的概念
- 切面(Aspect):切面代表横切关注点,它是独立于业务逻辑的模块,如日志、事务、缓存等。这些功能通常跨越多个类或模块。
- 连接点(Join Point):连接点是在程序执行过程中,可以插入切面代码的位置。在 C# 中,常见的连接点包括方法调用、异常抛出等。
- 切入点(Pointcut):切入点定义了在哪些连接点应用切面。通过切入点,可以指定哪些方法、类或模块需要执行某个横切关注点的逻辑。
- 通知(Advice):通知是具体要在切入点执行的代码,它描述了切面的行为。常见的通知类型有: 前置通知(Before Advice):在目标方法执行之前执行。
后置通知(After Advice):在目标方法执行之后执行。
异常通知(After Throwing Advice):在方法抛出异常时执行。
环绕通知(Around Advice):包围目标方法的执行,既可以在目标方法前,也可以在目标方法后执行。
- 织入(Weaving):织入是将切面代码插入到应用程序中的过程。在 C# 中,可以通过编译时、运行时或加载时织入切面。
常见框架:
如 PostSharp、AspectCore、Castle DynamicProxy 等