青少年编程与数学 02-020 C#程序设计基础 09课题、面向对象编程

发布于:2025-05-29 ⋅ 阅读:(21) ⋅ 点赞:(0)

青少年编程与数学 02-020 C#程序设计基础 09课题、面向对象编程

摘要:本文详细介绍了 C# 面向对象编程的核心概念和特性,包括类的定义、对象的创建和使用、继承、接口、多态、构造函数、析构函数、静态成员、抽象类、密封类、属性、索引器和事件等。通过丰富的代码示例,展示了如何在实际开发中应用这些特性,帮助读者更好地理解和掌握 C# 面向对象编程。

关键词:C#、面向对象编程、类、对象、继承、接口、多态、构造函数、析构函数、静态成员、抽象类、密封类、属性、索引器、事件

AI助手:Kimi、DeepSeek


一、概述

面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,它将数据和操作数据的方法组织成对象,通过对象之间的交互来实现程序的功能。以下是面向对象编程的几个核心概念:

1. 对象(Object)

  • 定义:对象是面向对象编程中的基本单位,它封装了数据和操作数据的方法。对象可以看作是一个具有特定属性和行为的实体。
  • 示例:在现实生活中,一辆汽车可以是一个对象。它的属性包括颜色、品牌、速度等,它的行为包括启动、加速、刹车等。

2. 类(Class)

  • 定义:类是对象的模板,它定义了一组对象的共同属性和方法。类是一个抽象的概念,而对象是类的具体实例。
  • 示例:如果“汽车”是一个类,那么“红色的宝马汽车”和“蓝色的丰田汽车”就是这个类的两个实例对象。

3. 封装(Encapsulation)

  • 定义:封装是将对象的属性和方法封装在一起,隐藏对象的内部实现细节,只暴露必要的接口供外部调用。封装可以保护对象的内部状态,防止外部的非法访问。
  • 示例:在一个银行账户类中,账户余额是一个私有属性,外部代码不能直接访问和修改它,而是通过一些方法(如存款、取款)来操作余额。

4. 继承(Inheritance)

  • 定义:继承是一种代码复用的机制,它允许一个类(子类)继承另一个类(父类)的属性和方法。子类可以扩展或修改父类的行为。
  • 示例:假设有一个“动物”类,它有一些通用的属性(如名字、年龄)和方法(如吃、睡)。然后定义一个“狗”类继承“动物”类,狗类可以继承动物类的所有属性和方法,并且可以添加一些新的行为(如叫)。

5. 多态(Polymorphism)

  • 定义:多态是指同一个接口可以被不同的底层实现所使用。它允许通过父类的接口调用子类的方法,具体调用哪个子类的方法取决于实际的对象类型。
  • 示例:假设有一个“动物”类有一个“发声”方法,不同的动物(如狗、猫)继承了这个方法并实现了自己的发声方式。当调用一个动物对象的“发声”方法时,具体调用哪个发声方式取决于这个动物对象的实际类型。

面向对象编程的优势

  • 代码复用性高:通过继承和类的模板机制,可以方便地复用代码。
  • 易于维护和扩展:封装使得对象的内部实现细节隐藏,修改对象的内部实现不会影响外部调用。
  • 模拟现实世界:面向对象的思维方式更接近现实世界,便于理解和设计复杂的系统。

常见的面向对象编程语言

  • Java:一种广泛使用的面向对象编程语言,具有跨平台、安全性和强大的类库。
  • C++:一种支持面向对象编程的高级语言,同时支持过程式编程,性能高,适用于系统开发和游戏开发。
  • Python:一种简洁易读的面向对象编程语言,支持多种编程范式,具有丰富的库和框架。
  • C#:由微软开发的面向对象编程语言,主要用于Windows平台的应用开发。

面向对象编程是一种强大的编程范式,它通过对象、类、封装、继承和多态等概念,使得程序设计更加模块化、可维护和可扩展。

二、C#与C++比较

C# 和 C++ 都是面向对象编程语言,但它们在设计目标、语法细节和运行环境等方面存在显著差异。以下是 C# 面向对象编程与 C++ 面向对象编程的主要区别:

1. 内存管理

C++:

  • 手动管理:C++ 依赖程序员手动管理内存分配和释放,使用 newdelete 操作符。如果程序员忘记释放内存,可能会导致内存泄漏;如果释放了未分配的内存,可能会导致程序崩溃。
  • 智能指针:C++11 引入了智能指针(如 std::shared_ptrstd::unique_ptr),但它们的使用需要程序员显式声明和管理。
  • 示例代码:
    int* ptr = new int(10);
    delete ptr;
    

C#:

  • 自动管理:C# 使用垃圾回收机制(Garbage Collection, GC)自动管理内存。程序员不需要手动释放对象,GC 会定期检查不再使用的对象并回收它们的内存。
  • 示例代码:
    int number = 10;
    

2. 语法简洁性

C++:

  • 复杂语法:C++ 的语法较为复杂,支持多种编程范式(面向对象、过程式、泛型等),这使得代码的可读性和可维护性相对较差。
  • 模板和宏:C++ 的模板和宏功能强大,但使用不当可能导致代码难以理解和维护。
  • 示例代码:
    template <typename T>
    class MyContainer {
    public:
        T value;
        MyContainer(T val) : value(val) {}
    };
    

C#:

  • 简洁语法:C# 的语法更加简洁明了,减少了模板和宏的使用,使得代码更易于阅读和维护。
  • 示例代码:
    public class MyContainer<T>
    {
        public T Value { get; set; }
    
        public MyContainer(T value)
        {
            Value = value;
        }
    }
    

3. 类型安全

C++:

  • 弱类型安全:C++ 允许隐式的类型转换,这可能导致一些难以发现的错误。
  • 示例代码:
    int a = 10;
    double b = a; // 隐式类型转换
    

C#:

  • 强类型安全:C# 严格要求类型匹配,不允许隐式的类型转换,这减少了类型错误的可能性。
  • 示例代码:
    int a = 10;
    double b = a; // 显式类型转换
    

4. 异常处理

C++:

  • 异常处理:C++ 使用 trycatchthrow 来处理异常,但异常处理机制相对复杂,且默认情况下不捕获所有异常。

  • 示例代码:

    try {
        throw std::runtime_error("An error occurred");
    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
    }
    

C#:

  • 完善的异常处理:C# 提供了完善的异常处理机制,所有异常都继承自 System.Exception 类,且默认情况下会捕获所有异常。
  • 示例代码:
    try {
        throw new Exception("An error occurred");
    } catch (Exception e) {
        Console.Error.WriteLine(e.Message);
    }
    

5. 多继承

C++:

  • 支持多继承:C++ 支持一个类继承多个父类,这在某些情况下非常有用,但也可能导致复杂的继承关系和潜在的二义性问题。
  • 示例代码:
    class Base1 {};
    class Base2 {};
    class Derived : public Base1, public Base2 {};
    

C#:

  • 不支持多继承:C# 不支持一个类继承多个父类,但可以通过接口来实现类似的功能。接口可以被多个类实现,从而实现多态。
  • 示例代码:
    public interface IBase1 {}
    public interface IBase2 {}
    public class Derived : IBase1, IBase2 {}
    

6. 委托和事件

C++:

  • 函数指针:C++ 使用函数指针或模板来实现回调机制,但这些机制相对复杂且不够灵活。
  • 示例代码:
    void callbackFunction() {
        // Callback implementation
    }
    
    void registerCallback(void (*callback)()) {
        // Register the callback
    }
    

C#:

  • 委托和事件:C# 提供了委托(Delegate)和事件(Event)机制,用于实现回调和事件处理,语法简洁且功能强大。
  • 示例代码:
    public delegate void MyDelegate();
    
    public class MyClass
    {
        public event MyDelegate MyEvent;
    
        public void TriggerEvent()
        {
            MyEvent?.Invoke();
        }
    }
    
    public class Program
    {
        public static void Main()
        {
            MyClass myClass = new MyClass();
            myClass.MyEvent += MyCallback;
            myClass.TriggerEvent();
        }
    
        public static void MyCallback()
        {
            Console.WriteLine("Event triggered");
        }
    }
    

7. 反射和动态类型

C++:

  • 有限的反射:C++ 的反射功能非常有限,主要通过模板和宏实现,但这些方法不够灵活且难以扩展。
  • 示例代码:
    template <typename T>
    void PrintType() {
        std::cout << typeid(T).name() << std::endl;
    }
    

C#:

  • 强大的反射:C# 提供了强大的反射机制,允许在运行时检查和操作类型、字段、方法等。此外,C# 还支持动态类型(dynamic),使得代码更加灵活。
  • 示例代码:
    using System.Reflection;
    
    public class MyClass
    {
        public string Name { get; set; }
    }
    
    public class Program
    {
        public static void Main()
        {
            MyClass myClass = new MyClass { Name = "Kimi" };
            Type type = myClass.GetType();
            PropertyInfo property = type.GetProperty("Name");
            Console.WriteLine(property.GetValue(myClass));
        }
    }
    

8. 跨平台支持

C++:

  • 跨平台:C++ 是一种跨平台语言,编写的代码可以在多种操作系统上运行,但需要针对不同的平台进行编译和优化。
  • 示例代码:
    #ifdef _WIN32
    // Windows-specific code
    #else
    // Unix-specific code
    #endif
    

C#:

  • 跨平台支持:C# 通过 .NET Core 和 .NET 5+ 提供了跨平台支持,使得 C# 程序可以在 Windows、Linux 和 macOS 上运行。此外,C# 还支持多种开发框架(如 ASP.NET Core、.NET MAUI 等)。
  • 示例代码:
    using System;
    
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine("Hello, world!");
        }
    }
    

9. 性能

C++:

  • 高性能:C++ 通常比 C# 更接近硬件,因此在性能上具有优势,尤其是在需要高性能计算和低延迟的场景中。
  • 示例代码:
    int main() {
        int sum = 0;
        for (int i = 0; i < 1000000; ++i) {
            sum += i;
        }
        return sum;
    }
    

C#:

  • 高性能:C# 的性能也很好,但通常不如 C++。C# 的垃圾回收机制可能会引入一些性能开销,但通过优化和配置可以减少这种影响。
  • 示例代码:
    public class Program
    {
        public static void Main()
        {
            int sum = 0;
            for (int i = 0; i < 1000000; ++i) {
                sum += i;
            }
            Console.WriteLine(sum);
        }
    }
    

10. 开发工具和生态系统

C++:

  • 开发工具:C++ 有多种开发工具,如 Visual Studio、CLion、Eclipse CDT 等,但这些工具的集成度和易用性不如 C# 的开发工具。
  • 生态系统:C++ 的生态系统非常庞大,涵盖了系统编程、游戏开发、嵌入式系统等多个领域。

C#:

  • 开发工具:C# 的开发工具非常强大,尤其是 Visual Studio 和 Visual Studio Code,提供了丰富的功能和良好的集成体验。
  • 生态系统:C# 的生态系统主要集中在 Windows 开发、Web 开发、移动开发等领域,尤其是 .NET Framework 和 .NET Core 的支持。

小结

C# 和 C++ 都是强大的面向对象编程语言,但它们在内存管理、语法简洁性、类型安全、异常处理、多继承、委托和事件、反射和动态类型、跨平台支持、性能、开发工具和生态系统等方面存在显著差异。C# 更适合快速开发和维护,而 C++ 更适合高性能和底层系统开发。选择哪种语言取决于具体的项目需求和开发环境。

三、类

在 C# 中,类(Class)是面向对象编程的核心概念之一。类是对象的模板,它定义了一组对象的共同属性和方法。通过类,可以创建具体的对象实例,并通过这些实例来操作数据和执行方法。以下是对 C# 中类的详细解释:

1. 定义类

类的定义使用 class 关键字,后面跟着类的名称和类体。类体包含类的成员,如属性、方法、构造函数、析构函数等。

示例代码:
public class Person
{
    // 属性
    public string Name { get; set; }
    public int Age { get; set; }

    // 方法
    public void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old.");
    }

    // 构造函数
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

2. 类的成员

类的成员包括属性、方法、构造函数、析构函数、字段、事件等。

2.1 属性(Properties)

属性是类的成员,用于封装类的字段,提供对字段的访问和修改。属性可以有 getset 访问器。

  • 自动实现的属性:

    public string Name { get; set; }
    
  • 手动实现的属性:

    private string name;
    public string Name
    {
        get { return name; }
        set { name = value; }
    }
    
2.2 方法(Methods)

方法是类的成员,用于定义类的行为。方法可以有参数和返回值。

  • 示例代码:
    public void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old.");
    }
    
2.3 构造函数(Constructors)

构造函数用于初始化类的实例。构造函数的名称与类名相同,没有返回值。

  • 无参构造函数:

    public Person()
    {
        Name = "Unknown";
        Age = 0;
    }
    
  • 带参构造函数:

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }
    
2.4 析构函数(Destructors)

析构函数用于在对象被销毁时执行清理操作。析构函数的名称与类名相同,但前面加一个波浪号 ~,没有参数和返回值。

  • 示例代码:
    ~Person()
    {
        Console.WriteLine("Person object is being destroyed.");
    }
    
2.5 字段(Fields)

字段是类的成员变量,用于存储类的状态。

  • 示例代码:
    private string name;
    private int age;
    
2.6 事件(Events)

事件用于实现类之间的通信,通常用于观察者模式。

  • 示例代码:
    public class Button
    {
        public event EventHandler Click;
    
        public void OnClick()
        {
            Click?.Invoke(this, EventArgs.Empty);
        }
    }
    

3. 访问修饰符

C# 提供了多种访问修饰符,用于控制类成员的访问权限。

  • public:公开访问,任何地方都可以访问。
  • private:私有访问,只能在类内部访问。
  • protected:受保护访问,只能在类内部或派生类中访问。
  • internal:内部访问,只能在当前程序集内访问。
  • protected internal:受保护的内部访问,只能在当前程序集内或派生类中访问。
示例代码:
public class Person
{
    public string Name { get; set; }
    private int age;

    public int Age
    {
        get { return age; }
        set { age = value; }
    }

    protected void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old.");
    }
}

4. 静态成员

静态成员属于类本身,而不是类的某个具体实例。静态成员可以通过类名直接访问,而不需要创建类的实例。

  • 静态字段:

    public static int Count = 0;
    
  • 静态方法:

    public static void PrintCount()
    {
        Console.WriteLine(Count);
    }
    
示例代码:
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
        Count++;
    }

    public static int Count = 0;

    public static void PrintCount()
    {
        Console.WriteLine(Count);
    }
}

public class Program
{
    public static void Main()
    {
        Person p1 = new Person("Alice", 30);
        Person p2 = new Person("Bob", 25);

        Person.PrintCount(); // 输出 2
    }
}

5. 继承

C# 支持类的继承,一个类可以继承另一个类的属性和方法。继承使用 : 符号表示。

  • 基类(Base Class):

    public class Animal
    {
        public string Name { get; set; }
    
        public virtual void MakeSound()
        {
            Console.WriteLine("Some generic sound");
        }
    }
    
  • 派生类(Derived Class):

    public class Dog : Animal
    {
        public override void MakeSound()
        {
            Console.WriteLine("Bark");
        }
    }
    
示例代码:
public class Animal
{
    public string Name { get; set; }

    public virtual void MakeSound()
    {
        Console.WriteLine("Some generic sound");
    }
}

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

public class Program
{
    public static void Main()
    {
        Animal myDog = new Dog();
        myDog.Name = "Buddy";
        myDog.MakeSound(); // 输出 "Bark"
    }
}

6. 接口(Interfaces)

接口是一种完全抽象的类型,它只定义方法和属性的签名,具体的实现由实现接口的类来完成。接口用于实现多态和代码复用。

  • 定义接口:

    public interface IAnimal
    {
        void MakeSound();
    }
    
  • 实现接口:

    public class Dog : IAnimal
    {
        public void MakeSound()
        {
            Console.WriteLine("Bark");
        }
    }
    
示例代码:
public interface IAnimal
{
    void MakeSound();
}

public class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

public class Cat : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Meow");
    }
}

public class Program
{
    public static void Main()
    {
        IAnimal myDog = new Dog();
        myDog.MakeSound(); // 输出 "Bark"

        IAnimal myCat = new Cat();
        myCat.MakeSound(); // 输出 "Meow"
    }
}

7. 抽象类(Abstract Classes)

抽象类是一种不能被实例化的类,它只能被继承。抽象类可以包含抽象方法和具体方法。抽象方法没有实现,必须在派生类中实现。

  • 定义抽象类:

    public abstract class Animal
    {
        public abstract void MakeSound();
    }
    
  • 继承抽象类:

    public class Dog : Animal
    {
        public override void MakeSound()
        {
            Console.WriteLine("Bark");
        }
    }
    
示例代码:
public abstract class Animal
{
    public abstract void MakeSound();
}

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

public class Program
{
    public static void Main()
    {
        Animal myDog = new Dog();
        myDog.MakeSound(); // 输出 "Bark"
    }
}

8. 密封类(Sealed Classes)

密封类是一种不能被继承的类,使用 sealed 关键字修饰。

  • 定义密封类:
    public sealed class Person
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
    
示例代码:
public sealed class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class Program
{
    public static void Main()
    {
        Person p = new Person { Name = "Alice", Age = 30 };
        Console.WriteLine($"{p.Name} is {p.Age} years old.");
    }
}

9. 嵌套类(Nested Classes)

嵌套类是定义在另一个类内部的类。嵌套类可以访问外部类的成员,但外部类不能访问嵌套类的成员。

  • 定义嵌套类:
    public class OuterClass
    {
        public class NestedClass
        {
            public void PrintMessage()
            {
                Console.WriteLine("Hello from nested class");
            }
        }
    }
    
示例代码:
public class OuterClass
{
    public class NestedClass
    {
        public void PrintMessage()
        {
            Console.WriteLine("Hello from nested class");
        }
    }
}

public class Program
{
    public static void Main()
    {
        OuterClass.NestedClass nested = new OuterClass.NestedClass();
        nested.PrintMessage(); // 输出 "Hello from nested class"
    }
}

10. 类的实例化

类的实例化是通过 new 关键字完成的,创建类的实例后,可以通过实例调用类的成员。

示例代码:
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old.");
    }
}

public class Program
{
    public static void Main()
    {
        Person p = new Person("Alice", 30);
        p.SayHello(); // 输出 "Hello, my name is Alice and I am 30 years old."
    }
}

小结

类是 C# 中面向对象编程的核心概念,它封装了数据和行为,提供了强大的功能和灵活性。通过类,可以创建对象实例,实现继承、多态、封装等面向对象的特性。掌握类的定义、成员、访问修饰符、静态成员、继承、接口、抽象类、密封类和嵌套类等概念,是理解和使用 C# 面向对象编程的基础。

四、类的静态成员

在 C# 中,类的静态成员是属于类本身而不是类的某个具体实例的成员。静态成员可以通过类名直接访问,而不需要创建类的实例。这使得静态成员在某些场景下非常有用,例如,当需要共享数据或提供全局访问点时。以下是对 C# 类的静态成员的详细解释:

1. 静态字段(Static Fields)

静态字段是属于类的字段,而不是某个实例的字段。静态字段在类加载时初始化,并且在类的整个生命周期中保持存在。

示例代码:
public class Counter
{
    // 静态字段
    public static int count = 0;

    // 实例字段
    public int instanceCount;

    // 构造函数
    public Counter()
    {
        instanceCount = ++count;
    }
}

public class Program
{
    public static void Main()
    {
        Counter c1 = new Counter();
        Console.WriteLine($"c1.instanceCount: {c1.instanceCount}, Counter.count: {Counter.count}");

        Counter c2 = new Counter();
        Console.WriteLine($"c2.instanceCount: {c2.instanceCount}, Counter.count: {Counter.count}");
    }
}
输出:
c1.instanceCount: 1, Counter.count: 1
c2.instanceCount: 2, Counter.count: 2

2. 静态方法(Static Methods)

静态方法是属于类的方法,而不是某个实例的方法。静态方法可以通过类名直接调用,而不需要创建类的实例。静态方法不能访问实例成员,但可以访问静态成员。

示例代码:
public class MathUtils
{
    // 静态方法
    public static int Add(int a, int b)
    {
        return a + b;
    }

    // 实例方法
    public int Multiply(int a, int b)
    {
        return a * b;
    }
}

public class Program
{
    public static void Main()
    {
        int sum = MathUtils.Add(5, 3); // 通过类名调用静态方法
        Console.WriteLine($"Sum: {sum}");

        MathUtils utils = new MathUtils();
        int product = utils.Multiply(5, 3); // 通过实例调用实例方法
        Console.WriteLine($"Product: {product}");
    }
}
输出:
Sum: 8
Product: 15

3. 静态构造函数(Static Constructors)

静态构造函数用于初始化类的静态成员。静态构造函数在类加载时自动调用,且只调用一次。静态构造函数没有参数,也没有返回值。

示例代码:
public class Logger
{
    // 静态字段
    public static string LogFilePath { get; private set; }

    // 静态构造函数
    static Logger()
    {
        LogFilePath = "log.txt";
        Console.WriteLine("Static constructor called. Log file path set to: " + LogFilePath);
    }

    // 实例方法
    public void Log(string message)
    {
        Console.WriteLine($"Logging to {LogFilePath}: {message}");
    }
}

public class Program
{
    public static void Main()
    {
        Logger logger = new Logger();
        logger.Log("This is a log message.");
    }
}
输出:
Static constructor called. Log file path set to: log.txt
Logging to log.txt: This is a log message.

4. 静态属性(Static Properties)

静态属性是属于类的属性,而不是某个实例的属性。静态属性可以通过类名直接访问,而不需要创建类的实例。静态属性可以有 getset 访问器。

示例代码:
public class Settings
{
    // 静态字段
    private static string _theme = "Light";

    // 静态属性
    public static string Theme
    {
        get { return _theme; }
        set { _theme = value; }
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine($"Current theme: {Settings.Theme}");

        Settings.Theme = "Dark";
        Console.WriteLine($"New theme: {Settings.Theme}");
    }
}
输出:
Current theme: Light
New theme: Dark

5. 静态类(Static Classes)

静态类是一种特殊的类,它只能包含静态成员,并且不能被实例化。静态类通常用于提供一组工具方法或常量。

示例代码:
public static class MathUtils
{
    public static int Add(int a, int b)
    {
        return a + b;
    }

    public static int Multiply(int a, int b)
    {
        return a * b;
    }
}

public class Program
{
    public static void Main()
    {
        int sum = MathUtils.Add(5, 3);
        Console.WriteLine($"Sum: {sum}");

        int product = MathUtils.Multiply(5, 3);
        Console.WriteLine($"Product: {product}");
    }
}
输出:
Sum: 8
Product: 15

6. 静态成员的访问规则

  • 静态成员属于类本身:静态成员可以通过类名直接访问,而不需要创建类的实例。
  • 静态成员不能访问实例成员:静态方法和静态属性不能访问实例字段和实例方法,因为实例成员需要具体的实例才能访问。
  • 静态成员在类加载时初始化:静态字段和静态构造函数在类加载时自动初始化,且只初始化一次。

7. 静态成员的用途

  • 共享数据:静态字段可以用于在类的所有实例之间共享数据。
  • 工具类:静态方法和静态类常用于提供工具方法,这些方法不需要依赖类的实例。
  • 全局访问点:静态成员可以提供全局访问点,例如单例模式中的静态实例。

小结

静态成员是 C# 中类的重要组成部分,它们提供了在类级别共享数据和行为的能力。通过静态字段、静态方法、静态构造函数、静态属性和静态类,可以实现多种功能,如共享数据、工具方法、全局访问点等。掌握静态成员的定义、访问规则和用途,是理解和使用 C# 面向对象编程的重要部分。

五、对象

在 C# 中,对象是类的实例,通过对象可以访问类的属性和方法。对象的创建和使用是面向对象编程的核心内容。以下是对 C# 中对象的创建和使用的详细解释:

1. 对象的创建

1.1 使用 new 关键字

在 C# 中,对象的创建通常使用 new 关键字。new 关键字会调用类的构造函数来初始化对象。

示例代码:
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    // 无参构造函数
    public Person()
    {
        Name = "Unknown";
        Age = 0;
    }

    // 带参构造函数
    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old.");
    }
}

public class Program
{
    public static void Main()
    {
        // 使用无参构造函数创建对象
        Person person1 = new Person();
        person1.Name = "Alice";
        person1.Age = 30;
        person1.SayHello(); // 输出 "Hello, my name is Alice and I am 30 years old."

        // 使用带参构造函数创建对象
        Person person2 = new Person("Bob", 25);
        person2.SayHello(); // 输出 "Hello, my name is Bob and I am 25 years old."
    }
}
1.2 使用对象初始化器

C# 提供了对象初始化器语法,可以在创建对象时直接初始化属性,而不需要显式调用构造函数。

示例代码:
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old.");
    }
}

public class Program
{
    public static void Main()
    {
        // 使用对象初始化器创建对象
        Person person1 = new Person { Name = "Alice", Age = 30 };
        person1.SayHello(); // 输出 "Hello, my name is Alice and I am 30 years old."

        // 如果类有带参构造函数,也可以结合使用
        Person person2 = new Person("Bob", 25) { Name = "Robert" };
        person2.SayHello(); // 输出 "Hello, my name is Robert and I am 25 years old."
    }
}

2. 对象的使用

2.1 访问属性

通过对象可以访问类的属性。属性可以是只读的(get),也可以是可读写的(getset)。

示例代码:
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class Program
{
    public static void Main()
    {
        Person person = new Person { Name = "Alice", Age = 30 };

        // 访问属性
        Console.WriteLine(person.Name); // 输出 "Alice"
        Console.WriteLine(person.Age);  // 输出 30

        // 修改属性
        person.Name = "Alicia";
        person.Age = 31;

        Console.WriteLine(person.Name); // 输出 "Alicia"
        Console.WriteLine(person.Age);  // 输出 31
    }
}
2.2 调用方法

通过对象可以调用类的方法。方法可以有参数,也可以返回值。

示例代码:
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name} and I am {Age} years old.");
    }

    public int GetAgeInMonths()
    {
        return Age * 12;
    }
}

public class Program
{
    public static void Main()
    {
        Person person = new Person { Name = "Alice", Age = 30 };

        // 调用方法
        person.SayHello(); // 输出 "Hello, my name is Alice and I am 30 years old."

        // 调用有返回值的方法
        int ageInMonths = person.GetAgeInMonths();
        Console.WriteLine($"Age in months: {ageInMonths}"); // 输出 "Age in months: 360"
    }
}
2.3 对象的比较

C# 提供了多种方式来比较对象,包括引用比较和值比较。

示例代码:
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class Program
{
    public static void Main()
    {
        Person person1 = new Person { Name = "Alice", Age = 30 };
        Person person2 = new Person { Name = "Alice", Age = 30 };
        Person person3 = person1;

        // 引用比较
        Console.WriteLine(person1 == person2); // 输出 "False",因为 person1 和 person2 是不同的实例
        Console.WriteLine(person1 == person3); // 输出 "True",因为 person1 和 person3 指向同一个实例

        // 值比较
        Console.WriteLine(person1.Name == person2.Name); // 输出 "True"
        Console.WriteLine(person1.Age == person2.Age);   // 输出 "True"
    }
}
2.4 对象的生命周期

对象的生命周期从创建开始,到垃圾回收结束。C# 使用垃圾回收机制(GC)自动管理对象的内存。

示例代码:
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    // 析构函数
    ~Person()
    {
        Console.WriteLine($"{Name} is being garbage collected.");
    }
}

public class Program
{
    public static void Main()
    {
        Person person = new Person("Alice", 30);
        person = null; // 使对象失去引用,等待垃圾回收

        // 强制垃圾回收
        GC.Collect();
    }
}
2.5 对象的克隆

C# 提供了克隆机制,可以通过 ICloneable 接口或手动实现克隆方法来创建对象的副本。

示例代码:
using System;

public class Person : ICloneable
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public object Clone()
    {
        return new Person(Name, Age);
    }
}

public class Program
{
    public static void Main()
    {
        Person person1 = new Person("Alice", 30);
        Person person2 = (Person)person1.Clone();

        Console.WriteLine(person1 == person2); // 输出 "False",因为 person1 和 person2 是不同的实例
        Console.WriteLine(person1.Name == person2.Name); // 输出 "True"
        Console.WriteLine(person1.Age == person2.Age);   // 输出 "True"
    }
}

3. 对象的销毁

C# 使用垃圾回收机制(GC)自动管理对象的销毁。当对象不再被任何引用指向时,GC 会自动回收其内存。

示例代码:
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    // 析构函数
    ~Person()
    {
        Console.WriteLine($"{Name} is being garbage collected.");
    }
}

public class Program
{
    public static void Main()
    {
        Person person = new Person("Alice", 30);
        person = null; // 使对象失去引用,等待垃圾回收

        // 强制垃圾回收
        GC.Collect();
    }
}

小结

在 C# 中,对象的创建和使用是面向对象编程的基础。通过 new 关键字和对象初始化器可以创建对象,通过对象可以访问类的属性和方法。对象的生命周期由垃圾回收机制管理,可以通过克隆机制创建对象的副本。掌握对象的创建、使用、比较、生命周期和销毁,是理解和使用 C# 面向对象编程的重要部分。

六、继承

在 C# 中,继承是面向对象编程的核心特性之一,它允许一个类(称为派生类或子类)继承另一个类(称为基类或父类)的属性和方法。通过继承,可以实现代码复用,提高开发效率,并且可以构建出层次化的类结构。以下是对 C# 中继承的详细解释:

1. 继承的基本语法

在 C# 中,继承使用 : 符号表示。派生类可以继承基类的属性和方法,但不能继承基类的构造函数和析构函数。

示例代码:
public class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    public virtual void MakeSound()
    {
        Console.WriteLine("Some generic sound");
    }
}

public class Dog : Animal
{
    public Dog(string name) : base(name) // 调用基类的构造函数
    {
    }

    public override void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

public class Cat : Animal
{
    public Cat(string name) : base(name)
    {
    }

    public override void MakeSound()
    {
        Console.WriteLine("Meow");
    }
}

public class Program
{
    public static void Main()
    {
        Animal myDog = new Dog("Buddy");
        myDog.MakeSound(); // 输出 "Bark"

        Animal myCat = new Cat("Whiskers");
        myCat.MakeSound(); // 输出 "Meow"
    }
}

2. 继承的特点

2.1 代码复用

派生类继承了基类的属性和方法,减少了重复代码的编写。

2.2 扩展性

派生类可以扩展基类的功能,通过添加新的属性和方法,或者重写基类的方法来实现特定的行为。

2.3 多态性

继承是实现多态的基础。通过基类的引用可以调用派生类的方法,具体调用哪个方法取决于实际的对象类型。

3. 构造函数和继承

派生类的构造函数可以调用基类的构造函数来初始化基类的成员。这通过 base 关键字实现。

示例代码:
public class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }
}

public class Dog : Animal
{
    public Dog(string name) : base(name) // 调用基类的构造函数
    {
    }
}

public class Program
{
    public static void Main()
    {
        Dog myDog = new Dog("Buddy");
        Console.WriteLine(myDog.Name); // 输出 "Buddy"
    }
}

4. 方法重写(Override)

派生类可以重写基类中的虚方法(使用 virtual 关键字声明的方法)。重写方法使用 override 关键字。

示例代码:
public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Some generic sound");
    }
}

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

public class Program
{
    public static void Main()
    {
        Animal myDog = new Dog();
        myDog.MakeSound(); // 输出 "Bark"
    }
}

5. 方法隐藏(Hide)

如果派生类中定义了一个与基类同名的方法,但没有使用 override 关键字,那么派生类的方法会隐藏基类的方法。这称为方法隐藏,使用 new 关键字显式表示。

示例代码:
public class Animal
{
    public void MakeSound()
    {
        Console.WriteLine("Some generic sound");
    }
}

public class Dog : Animal
{
    public new void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

public class Program
{
    public static void Main()
    {
        Animal myDog = new Dog();
        myDog.MakeSound(); // 输出 "Some generic sound"

        Dog myDog2 = new Dog();
        myDog2.MakeSound(); // 输出 "Bark"
    }
}

6. 抽象类和抽象方法

抽象类是不能被实例化的类,它只能被继承。抽象类可以包含抽象方法(使用 abstract 关键字声明的方法),这些方法没有实现,必须在派生类中实现。

示例代码:
public abstract class Animal
{
    public abstract void MakeSound();
}

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

public class Program
{
    public static void Main()
    {
        Animal myDog = new Dog();
        myDog.MakeSound(); // 输出 "Bark"
    }
}

7. 密封类(Sealed Classes)

密封类是不能被继承的类,使用 sealed 关键字修饰。

示例代码:
public sealed class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public class Program
{
    public static void Main()
    {
        Person person = new Person { Name = "Alice", Age = 30 };
        Console.WriteLine($"{person.Name} is {person.Age} years old.");
    }
}

8. 继承的限制

  • C# 不支持多重继承:一个类只能继承一个基类,但可以通过接口实现类似的功能。
  • 基类的构造函数必须被调用:派生类的构造函数必须显式或隐式调用基类的构造函数。
  • 抽象类不能被实例化:抽象类只能被继承,不能直接创建实例。
  • 密封类不能被继承:密封类不能有派生类。

总结

继承是 C# 面向对象编程中的一个重要特性,它允许派生类继承基类的属性和方法,实现代码复用和扩展性。通过方法重写和多态性,可以实现灵活的类设计。掌握继承的基本语法、构造函数的调用、方法重写、抽象类、密封类和接口的使用,是理解和使用 C# 面向对象编程的关键部分。

七、接口

在 C# 中,接口(Interface)是一种完全抽象的类型,它定义了一组方法、属性、索引器和事件的签名,但不提供具体的实现。接口的主要作用是规范实现类的行为,确保实现类提供接口中定义的所有成员的实现。接口是实现多态和代码复用的重要机制。以下是对 C# 中接口的详细解释:

1. 接口的定义

接口使用 interface 关键字定义。接口中的成员(方法、属性、索引器和事件)默认是 public 的,不能有访问修饰符,并且不能有实现。

示例代码:
public interface IAnimal
{
    void MakeSound(); // 方法签名
    string Name { get; set; } // 属性签名
    event EventHandler OnSound; // 事件签名
}

2. 实现接口

类可以通过 : 符号实现接口,并提供接口中定义的所有成员的具体实现。

示例代码:
public class Dog : IAnimal
{
    public string Name { get; set; }

    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }

    public event EventHandler OnSound;

    protected virtual void OnMakeSound(EventArgs e)
    {
        OnSound?.Invoke(this, e);
    }
}

public class Cat : IAnimal
{
    public string Name { get; set; }

    public void MakeSound()
    {
        Console.WriteLine("Meow");
    }

    public event EventHandler OnSound;

    protected virtual void OnMakeSound(EventArgs e)
    {
        OnSound?.Invoke(this, e);
    }
}

3. 接口的特点

3.1 完全抽象

接口中的成员没有实现,具体的实现由实现接口的类提供。

3.2 多继承

C# 不支持多重继承,但一个类可以实现多个接口,从而实现类似多重继承的功能。

3.3 规范行为

接口的主要作用是规范实现类的行为,确保实现类提供接口中定义的所有成员的实现。

4. 接口的使用场景

4.1 定义通用行为

接口可以定义一组通用的行为,多个类可以实现这些行为,从而实现多态。

示例代码:
public interface IAnimal
{
    void MakeSound();
}

public class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

public class Cat : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Meow");
    }
}

public class Program
{
    public static void Main()
    {
        IAnimal myDog = new Dog();
        myDog.MakeSound(); // 输出 "Bark"

        IAnimal myCat = new Cat();
        myCat.MakeSound(); // 输出 "Meow"
    }
}
4.2 依赖注入

接口常用于依赖注入,通过接口可以实现松耦合的设计,使得代码更容易测试和维护。

示例代码:
public interface IAnimal
{
    void MakeSound();
}

public class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

public class AnimalSoundMaker
{
    private IAnimal animal;

    public AnimalSoundMaker(IAnimal animal)
    {
        this.animal = animal;
    }

    public void MakeSound()
    {
        animal.MakeSound();
    }
}

public class Program
{
    public static void Main()
    {
        IAnimal myDog = new Dog();
        AnimalSoundMaker soundMaker = new AnimalSoundMaker(myDog);
        soundMaker.MakeSound(); // 输出 "Bark"
    }
}

5. 接口的成员

接口可以包含以下类型的成员:

  • 方法:定义方法的签名。
  • 属性:定义属性的签名。
  • 索引器:定义索引器的签名。
  • 事件:定义事件的签名。
示例代码:
public interface IAnimal
{
    void MakeSound(); // 方法签名
    string Name { get; set; } // 属性签名
    string this[int index] { get; set; } // 索引器签名
    event EventHandler OnSound; // 事件签名
}

6. 显式接口实现

类可以显式实现接口的成员,这样实现的成员只能通过接口类型的引用访问,而不能通过类类型的引用访问。

示例代码:
public interface IAnimal
{
    void MakeSound();
}

public class Dog : IAnimal
{
    void IAnimal.MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

public class Program
{
    public static void Main()
    {
        Dog myDog = new Dog();
        // myDog.MakeSound(); // 错误:Dog 类中没有 MakeSound 方法

        IAnimal animal = myDog;
        animal.MakeSound(); // 输出 "Bark"
    }
}

7. 接口的继承

接口可以继承其他接口,从而扩展接口的功能。

示例代码:
public interface IAnimal
{
    void MakeSound();
}

public interface IDomesticAnimal : IAnimal
{
    void GreetOwner();
}

public class Dog : IDomesticAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }

    public void GreetOwner()
    {
        Console.WriteLine("Wag tail");
    }
}

public class Program
{
    public static void Main()
    {
        Dog myDog = new Dog();
        myDog.MakeSound(); // 输出 "Bark"
        myDog.GreetOwner(); // 输出 "Wag tail"
    }
}

8. 接口与抽象类的区别

  • 接口:

    • 完全抽象,不能有实现。
    • 可以包含方法、属性、索引器和事件的签名。
    • 一个类可以实现多个接口。
    • 用于规范行为,实现多态。
  • 抽象类:

    • 可以包含抽象方法和具体方法。
    • 可以包含字段和属性。
    • 一个类只能继承一个抽象类。
    • 用于提供默认实现,实现代码复用。

9. 接口的默认实现(C# 8.0+)

从 C# 8.0 开始,接口可以提供默认实现。这使得接口不仅可以定义行为,还可以提供默认的实现,从而减少重复代码。

示例代码:
public interface IAnimal
{
    void MakeSound()
    {
        Console.WriteLine("Some generic sound");
    }
}

public class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

public class Cat : IAnimal
{
    // 使用接口的默认实现
}

public class Program
{
    public static void Main()
    {
        IAnimal myDog = new Dog();
        myDog.MakeSound(); // 输出 "Bark"

        IAnimal myCat = new Cat();
        myCat.MakeSound(); // 输出 "Some generic sound"
    }
}

10. 接口的使用注意事项

  • 接口的成员默认是 public 的,不能有访问修饰符。
  • 接口不能包含字段。
  • 接口不能有构造函数和析构函数。
  • 接口的成员可以有默认实现,但实现类可以选择重写这些方法。

小结

接口是 C# 中实现多态和代码复用的重要机制。通过接口,可以定义一组通用的行为,多个类可以实现这些行为,从而实现多态。接口的主要作用是规范实现类的行为,确保实现类提供接口中定义的所有成员的实现。掌握接口的定义、实现、成员、显式实现、继承和默认实现,是理解和使用 C# 面向对象编程的重要部分。

八、多态

在 C# 中,多态(Polymorphism)是面向对象编程的核心特性之一,它允许通过基类的引用调用派生类的方法,具体调用哪个方法取决于实际的对象类型。多态使得程序更加灵活和可扩展,能够以统一的方式处理不同类型的对象。以下是对 C# 中多态的详细解释:

1. 多态的定义

多态是指同一个接口可以被不同的底层实现所使用。具体来说,多态允许通过基类的引用调用派生类的方法,具体调用哪个方法取决于实际的对象类型。

2. 多态的实现方式

在 C# 中,多态主要通过以下两种方式实现:

  • 方法重写(Override)
  • 接口实现

3. 方法重写(Override)

3.1 虚方法(Virtual Methods)

虚方法是使用 virtual 关键字声明的方法,可以在派生类中被重写。

3.2 重写方法(Override Methods)

派生类使用 override 关键字重写基类中的虚方法。

示例代码:
public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Some generic sound");
    }
}

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

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

public class Program
{
    public static void Main()
    {
        Animal myDog = new Dog();
        myDog.MakeSound(); // 输出 "Bark"

        Animal myCat = new Cat();
        myCat.MakeSound(); // 输出 "Meow"
    }
}

4. 接口实现

接口是一种完全抽象的类型,它定义了一组方法、属性、索引器和事件的签名,但不提供具体的实现。通过接口实现,可以实现多态。

示例代码:
public interface IAnimal
{
    void MakeSound();
}

public class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

public class Cat : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Meow");
    }
}

public class Program
{
    public static void Main()
    {
        IAnimal myDog = new Dog();
        myDog.MakeSound(); // 输出 "Bark"

        IAnimal myCat = new Cat();
        myCat.MakeSound(); // 输出 "Meow"
    }
}

5. 多态的优势

5.1 代码复用

通过多态,可以编写通用的代码,减少重复代码的编写。

5.2 可扩展性

多态使得程序更加灵活,可以方便地添加新的类,而不需要修改现有代码。

5.3 松耦合

多态使得类之间的耦合度降低,提高了代码的可维护性和可测试性。

6. 多态的实现机制

6.1 运行时多态(Runtime Polymorphism)

运行时多态是指在运行时根据对象的实际类型调用相应的方法。这是通过虚方法和重写方法实现的。

示例代码:
public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Some generic sound");
    }
}

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

public class Program
{
    public static void Main()
    {
        Animal myDog = new Dog();
        myDog.MakeSound(); // 输出 "Bark"
    }
}
6.2 编译时多态(Compile-time Polymorphism)

编译时多态是指在编译时根据方法的签名调用相应的方法。这是通过方法重载实现的。

示例代码:
public class MathUtils
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public double Add(double a, double b)
    {
        return a + b;
    }
}

public class Program
{
    public static void Main()
    {
        MathUtils utils = new MathUtils();
        int sum1 = utils.Add(5, 3); // 调用 int 版本的 Add 方法
        double sum2 = utils.Add(5.5, 3.3); // 调用 double 版本的 Add 方法

        Console.WriteLine($"Sum1: {sum1}"); // 输出 "Sum1: 8"
        Console.WriteLine($"Sum2: {sum2}"); // 输出 "Sum2: 8.8"
    }
}

7. 多态的使用场景

7.1 通用方法

通过基类或接口的引用,可以编写通用的方法,这些方法可以处理不同类型的对象。

示例代码:
public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Some generic sound");
    }
}

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

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

public class Program
{
    public static void MakeAnimalSound(Animal animal)
    {
        animal.MakeSound();
    }

    public static void Main()
    {
        Dog myDog = new Dog();
        Cat myCat = new Cat();

        MakeAnimalSound(myDog); // 输出 "Bark"
        MakeAnimalSound(myCat); // 输出 "Meow"
    }
}
7.2 依赖注入

通过接口或基类的引用,可以实现依赖注入,使得代码更加灵活和可测试。

示例代码:
public interface IAnimal
{
    void MakeSound();
}

public class Dog : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Bark");
    }
}

public class Cat : IAnimal
{
    public void MakeSound()
    {
        Console.WriteLine("Meow");
    }
}

public class AnimalSoundMaker
{
    private IAnimal animal;

    public AnimalSoundMaker(IAnimal animal)
    {
        this.animal = animal;
    }

    public void MakeSound()
    {
        animal.MakeSound();
    }
}

public class Program
{
    public static void Main()
    {
        IAnimal myDog = new Dog();
        AnimalSoundMaker soundMaker = new AnimalSoundMaker(myDog);
        soundMaker.MakeSound(); // 输出 "Bark"
    }
}

8. 多态的注意事项

8.1 虚方法和重写方法

虚方法使用 virtual 关键字声明,重写方法使用 override 关键字声明。

8.2 接口实现

接口中的成员默认是 public 的,不能有访问修饰符。

8.3 密封类

密封类(sealed)不能被继承,因此不能在密封类中使用虚方法。

8.4 抽象类

抽象类可以包含虚方法和抽象方法,派生类必须实现抽象方法。

9. 多态的高级应用

9.1 策略模式

策略模式是一种行为设计模式,通过定义一系列算法,并将每个算法封装起来,使它们可以互换使用。策略模式利用了多态的特性。

示例代码:
public interface IStrategy
{
    void Execute(int a, int b);
}

public class AddStrategy : IStrategy
{
    public void Execute(int a, int b)
    {
        Console.WriteLine($"Result: {a + b}");
    }
}

public class SubtractStrategy : IStrategy
{
    public void Execute(int a, int b)
    {
        Console.WriteLine($"Result: {a - b}");
    }
}

public class Context
{
    private IStrategy strategy;

    public Context(IStrategy strategy)
    {
        this.strategy = strategy;
    }

    public void SetStrategy(IStrategy strategy)
    {
        this.strategy = strategy;
    }

    public void ExecuteStrategy(int a, int b)
    {
        strategy.Execute(a, b);
    }
}

public class Program
{
    public static void Main()
    {
        IStrategy addStrategy = new AddStrategy();
        IStrategy subtractStrategy = new SubtractStrategy();

        Context context = new Context(addStrategy);
        context.ExecuteStrategy(10, 5); // 输出 "Result: 15"

        context.SetStrategy(subtractStrategy);
        context.ExecuteStrategy(10, 5); // 输出 "Result: 5"
    }
}

小结

多态是 C# 面向对象编程中的一个重要特性,它允许通过基类的引用调用派生类的方法,具体调用哪个方法取决于实际的对象类型。多态主要通过方法重写和接口实现来实现,具有代码复用、可扩展性和松耦合等优势。掌握多态的实现方式、使用场景和高级应用,是理解和使用 C# 面向对象编程的关键部分。

九、综合示例

以下是一个综合示例代码,全面展示了 C# 面向对象编程的各种相关特性,包括类的定义、对象的创建和使用、继承、接口、多态、构造函数、析构函数、静态成员、抽象类、密封类、属性、索引器、事件等:

using System;

// 定义一个接口
public interface IAnimal
{
    void MakeSound();
    string Name { get; set; }
}

// 定义一个抽象类
public abstract class Animal : IAnimal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    public abstract void MakeSound();

    // 静态字段
    public static int AnimalCount { get; private set; } = 0;

    // 构造函数
    public Animal()
    {
        AnimalCount++;
    }

    // 析构函数
    ~Animal()
    {
        AnimalCount--;
        Console.WriteLine($"{Name} is being destroyed. Remaining animals: {AnimalCount}");
    }

    // 事件
    public event EventHandler OnSound;

    protected virtual void OnMakeSound(EventArgs e)
    {
        OnSound?.Invoke(this, e);
    }
}

// 定义一个派生类
public class Dog : Animal
{
    public Dog(string name) : base(name)
    {
    }

    public override void MakeSound()
    {
        Console.WriteLine("Bark");
        OnMakeSound(EventArgs.Empty);
    }
}

// 定义另一个派生类
public class Cat : Animal
{
    public Cat(string name) : base(name)
    {
    }

    public override void MakeSound()
    {
        Console.WriteLine("Meow");
        OnMakeSound(EventArgs.Empty);
    }
}

// 定义一个密封类
public sealed class Bulldog : Dog
{
    public Bulldog(string name) : base(name)
    {
    }

    public new void MakeSound()
    {
        Console.WriteLine("Woof");
    }
}

// 定义一个类实现接口
public class Duck : IAnimal
{
    public string Name { get; set; }

    public Duck(string name)
    {
        Name = name;
    }

    public void MakeSound()
    {
        Console.WriteLine("Quack");
    }
}

// 定义一个类使用接口
public class AnimalSoundMaker
{
    private IAnimal animal;

    public AnimalSoundMaker(IAnimal animal)
    {
        this.animal = animal;
    }

    public void MakeSound()
    {
        animal.MakeSound();
    }
}

public class Program
{
    public static void Main()
    {
        // 创建对象
        IAnimal dog = new Dog("Buddy");
        IAnimal cat = new Cat("Whiskers");
        IAnimal duck = new Duck("Donald");

        // 调用方法
        dog.MakeSound(); // 输出 "Bark"
        cat.MakeSound(); // 输出 "Meow"
        duck.MakeSound(); // 输出 "Quack"

        // 使用 AnimalSoundMaker 类
        AnimalSoundMaker soundMaker = new AnimalSoundMaker(dog);
        soundMaker.MakeSound(); // 输出 "Bark"

        soundMaker = new AnimalSoundMaker(cat);
        soundMaker.MakeSound(); // 输出 "Meow"

        // 静态成员
        Console.WriteLine($"Total animals: {Animal.AnimalCount}"); // 输出 "Total animals: 3"

        // 密封类
        Bulldog bulldog = new Bulldog("Spike");
        bulldog.MakeSound(); // 输出 "Woof"

        // 事件
        dog.OnSound += (sender, e) => Console.WriteLine($"{((Animal)sender).Name} made a sound.");
        dog.MakeSound(); // 输出 "Bark" 和 "Buddy made a sound."

        // 对象销毁
        dog = null;
        cat = null;
        duck = null;
        bulldog = null;

        // 强制垃圾回收
        GC.Collect();
    }
}

代码说明

  1. 接口(IAnimal):

    • 定义了 MakeSound 方法和 Name 属性的签名。
    • Duck 类实现了 IAnimal 接口。
  2. 抽象类(Animal):

    • 定义了 MakeSound 抽象方法和 Name 属性。
    • 包含静态字段 AnimalCount,用于统计动物的数量。
    • 包含构造函数和析构函数,用于管理对象的生命周期。
    • 包含事件 OnSound,用于通知其他对象动物发声。
  3. 派生类(DogCat):

    • 继承自 Animal 类,并重写了 MakeSound 方法。
    • 调用了基类的构造函数。
  4. 密封类(Bulldog):

    • 继承自 Dog 类,但被声明为密封类,不能被进一步继承。
    • 重写了 MakeSound 方法。
  5. 类实现接口(Duck):

    • 实现了 IAnimal 接口,提供了 MakeSound 方法和 Name 属性的具体实现。
  6. 类使用接口(AnimalSoundMaker):

    • 通过依赖注入,使用 IAnimal 接口的引用调用 MakeSound 方法。
  7. 多态:

    • 通过基类的引用调用派生类的方法,具体调用哪个方法取决于实际的对象类型。
  8. 静态成员:

    • AnimalCount 是一个静态字段,用于统计动物的数量。
  9. 事件:

    • OnSound 是一个事件,用于通知其他对象动物发声。
  10. 对象销毁:

    • 通过将对象引用设置为 null,使对象失去引用,等待垃圾回收。
    • 调用 GC.Collect() 强制垃圾回收。

输出示例

运行上述代码,输出可能如下:

Bark
Meow
Quack
Bark
Meow
Total animals: 3
Woof
Bark
Buddy made a sound.
Whiskers is being destroyed. Remaining animals: 2
Donald is being destroyed. Remaining animals: 1
Buddy is being destroyed. Remaining animals: 0
Spike is being destroyed. Remaining animals: 0

这个示例代码展示了 C# 面向对象编程的多种特性,包括类的定义、对象的创建和使用、继承、接口、多态、构造函数、析构函数、静态成员、抽象类、密封类、属性、索引器和事件等。通过这个示例,可以更好地理解和掌握 C# 面向对象编程的核心概念。

总结

本文全面介绍了 C# 面向对象编程的核心概念和特性,包括类的定义、对象的创建和使用、继承、接口、多态、构造函数、析构函数、静态成员、抽象类、密封类、属性、索引器和事件等。通过详细的代码示例,展示了如何在实际开发中应用这些特性。类是面向对象编程的基础,通过类可以创建对象实例,实现封装、继承和多态等特性。接口和抽象类用于规范行为,实现多态和代码复用。静态成员和密封类提供了全局访问点和不可继承的特性。多态通过方法重写和接口实现,使得程序更加灵活和可扩展。通过综合示例代码,读者可以更好地理解和掌握这些概念,提升面向对象编程的能力。


网站公告

今日签到

点亮在社区的每一天
去签到