青少年编程与数学 02-020 C#程序设计基础 09课题、面向对象编程
青少年编程与数学 02-020 C#程序设计基础 09课题、面向对象编程
- 一、概述
- 1. 对象(Object)
- 2. 类(Class)
- 3. 封装(Encapsulation)
- 4. 继承(Inheritance)
- 5. 多态(Polymorphism)
- 面向对象编程的优势
- 常见的面向对象编程语言
- 二、C#与C++比较
- 1. 内存管理
- 2. 语法简洁性
- 3. 类型安全
- 4. 异常处理
- 5. 多继承
- 6. 委托和事件
- 7. 反射和动态类型
- 8. 跨平台支持
- 9. 性能
- 10. 开发工具和生态系统
- 小结
- 三、类
- 1. 定义类
- 示例代码:
- 2. 类的成员
- 2.1 属性(Properties)
- 2.2 方法(Methods)
- 2.3 构造函数(Constructors)
- 2.4 析构函数(Destructors)
- 2.5 字段(Fields)
- 2.6 事件(Events)
- 3. 访问修饰符
- 示例代码:
- 4. 静态成员
- 示例代码:
- 5. 继承
- 示例代码:
- 6. 接口(Interfaces)
- 示例代码:
- 7. 抽象类(Abstract Classes)
- 示例代码:
- 8. 密封类(Sealed Classes)
- 示例代码:
- 9. 嵌套类(Nested Classes)
- 示例代码:
- 10. 类的实例化
- 示例代码:
- 小结
- 四、类的静态成员
- 1. 静态字段(Static Fields)
- 示例代码:
- 输出:
- 2. 静态方法(Static Methods)
- 示例代码:
- 输出:
- 3. 静态构造函数(Static Constructors)
- 示例代码:
- 输出:
- 4. 静态属性(Static Properties)
- 示例代码:
- 输出:
- 5. 静态类(Static Classes)
- 示例代码:
- 输出:
- 6. 静态成员的访问规则
- 7. 静态成员的用途
- 小结
- 五、对象
- 1. 对象的创建
- 1.1 使用 `new` 关键字
- 示例代码:
- 1.2 使用对象初始化器
- 示例代码:
- 2. 对象的使用
- 2.1 访问属性
- 示例代码:
- 2.2 调用方法
- 示例代码:
- 2.3 对象的比较
- 示例代码:
- 2.4 对象的生命周期
- 示例代码:
- 2.5 对象的克隆
- 示例代码:
- 3. 对象的销毁
- 示例代码:
- 小结
- 六、继承
- 1. 继承的基本语法
- 示例代码:
- 2. 继承的特点
- 2.1 代码复用
- 2.2 扩展性
- 2.3 多态性
- 3. 构造函数和继承
- 示例代码:
- 4. 方法重写(Override)
- 示例代码:
- 5. 方法隐藏(Hide)
- 示例代码:
- 6. 抽象类和抽象方法
- 示例代码:
- 7. 密封类(Sealed Classes)
- 示例代码:
- 8. 继承的限制
- 总结
- 七、接口
- 1. 接口的定义
- 示例代码:
- 2. 实现接口
- 示例代码:
- 3. 接口的特点
- 3.1 完全抽象
- 3.2 多继承
- 3.3 规范行为
- 4. 接口的使用场景
- 4.1 定义通用行为
- 示例代码:
- 4.2 依赖注入
- 示例代码:
- 5. 接口的成员
- 示例代码:
- 6. 显式接口实现
- 示例代码:
- 7. 接口的继承
- 示例代码:
- 8. 接口与抽象类的区别
- 9. 接口的默认实现(C# 8.0+)
- 示例代码:
- 10. 接口的使用注意事项
- 小结
- 八、多态
- 1. 多态的定义
- 2. 多态的实现方式
- 3. 方法重写(Override)
- 3.1 虚方法(Virtual Methods)
- 3.2 重写方法(Override Methods)
- 示例代码:
- 4. 接口实现
- 示例代码:
- 5. 多态的优势
- 5.1 代码复用
- 5.2 可扩展性
- 5.3 松耦合
- 6. 多态的实现机制
- 6.1 运行时多态(Runtime Polymorphism)
- 示例代码:
- 6.2 编译时多态(Compile-time Polymorphism)
- 示例代码:
- 7. 多态的使用场景
- 7.1 通用方法
- 示例代码:
- 7.2 依赖注入
- 示例代码:
- 8. 多态的注意事项
- 8.1 虚方法和重写方法
- 8.2 接口实现
- 8.3 密封类
- 8.4 抽象类
- 9. 多态的高级应用
- 9.1 策略模式
- 示例代码:
- 小结
- 九、综合示例
- 代码说明
- 输出示例
- 总结
摘要:本文详细介绍了 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++ 依赖程序员手动管理内存分配和释放,使用
new
和delete
操作符。如果程序员忘记释放内存,可能会导致内存泄漏;如果释放了未分配的内存,可能会导致程序崩溃。 - 智能指针:C++11 引入了智能指针(如
std::shared_ptr
和std::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++ 使用
try
、catch
和throw
来处理异常,但异常处理机制相对复杂,且默认情况下不捕获所有异常。 -
示例代码:
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)
属性是类的成员,用于封装类的字段,提供对字段的访问和修改。属性可以有 get
和 set
访问器。
-
自动实现的属性:
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)
静态属性是属于类的属性,而不是某个实例的属性。静态属性可以通过类名直接访问,而不需要创建类的实例。静态属性可以有 get
和 set
访问器。
示例代码:
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
),也可以是可读写的(get
和 set
)。
示例代码:
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();}
}
代码说明
-
接口(
IAnimal
):- 定义了
MakeSound
方法和Name
属性的签名。 Duck
类实现了IAnimal
接口。
- 定义了
-
抽象类(
Animal
):- 定义了
MakeSound
抽象方法和Name
属性。 - 包含静态字段
AnimalCount
,用于统计动物的数量。 - 包含构造函数和析构函数,用于管理对象的生命周期。
- 包含事件
OnSound
,用于通知其他对象动物发声。
- 定义了
-
派生类(
Dog
和Cat
):- 继承自
Animal
类,并重写了MakeSound
方法。 - 调用了基类的构造函数。
- 继承自
-
密封类(
Bulldog
):- 继承自
Dog
类,但被声明为密封类,不能被进一步继承。 - 重写了
MakeSound
方法。
- 继承自
-
类实现接口(
Duck
):- 实现了
IAnimal
接口,提供了MakeSound
方法和Name
属性的具体实现。
- 实现了
-
类使用接口(
AnimalSoundMaker
):- 通过依赖注入,使用
IAnimal
接口的引用调用MakeSound
方法。
- 通过依赖注入,使用
-
多态:
- 通过基类的引用调用派生类的方法,具体调用哪个方法取决于实际的对象类型。
-
静态成员:
AnimalCount
是一个静态字段,用于统计动物的数量。
-
事件:
OnSound
是一个事件,用于通知其他对象动物发声。
-
对象销毁:
- 通过将对象引用设置为
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# 面向对象编程的核心概念和特性,包括类的定义、对象的创建和使用、继承、接口、多态、构造函数、析构函数、静态成员、抽象类、密封类、属性、索引器和事件等。通过详细的代码示例,展示了如何在实际开发中应用这些特性。类是面向对象编程的基础,通过类可以创建对象实例,实现封装、继承和多态等特性。接口和抽象类用于规范行为,实现多态和代码复用。静态成员和密封类提供了全局访问点和不可继承的特性。多态通过方法重写和接口实现,使得程序更加灵活和可扩展。通过综合示例代码,读者可以更好地理解和掌握这些概念,提升面向对象编程的能力。