青少年编程与数学 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# 面向对象编程的核心概念和特性,包括类的定义、对象的创建和使用、继承、接口、多态、构造函数、析构函数、静态成员、抽象类、密封类、属性、索引器和事件等。通过详细的代码示例,展示了如何在实际开发中应用这些特性。类是面向对象编程的基础,通过类可以创建对象实例,实现封装、继承和多态等特性。接口和抽象类用于规范行为,实现多态和代码复用。静态成员和密封类提供了全局访问点和不可继承的特性。多态通过方法重写和接口实现,使得程序更加灵活和可扩展。通过综合示例代码,读者可以更好地理解和掌握这些概念,提升面向对象编程的能力。
相关文章:
青少年编程与数学 02-020 C#程序设计基础 09课题、面向对象编程
青少年编程与数学 02-020 C#程序设计基础 09课题、面向对象编程 一、概述1. 对象(Object)2. 类(Class)3. 封装(Encapsulation)4. 继承(Inheritance)5. 多态(Polymorphism…...

Denoising Autoencoders 视频截图 DAEs简单实现 kaggle 去噪编码器
https://www.bilibili.com/video/BV1syzrYaEtw Denoising Autoencoders (DAEs) 是一种无监督学习模型,属于自动编码器(Autoencoder)的一种扩展形式。它们的目标是通过训练神经网络来学习数据的鲁棒表示(robust representation&a…...

GoogLeNet网络模型
GoogLeNet网络模型 诞生背景 在2014年的ImageNet图像识别挑战赛中,一个GoogLeNet的网络架构大放异彩,与VGG不同的是,VGG用的是3*3的卷积,而GoogLeNet从1*1到7*7的卷积核都用,也就是使用不同大小的卷积核组合。 网络…...
LeetCode Hot100 (贪心)
121. 买卖股票的最佳时机 题意 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从…...

仿真科普|弥合市场需求断层,高性能仿真,“性能”与“安全”如何兼得?
2025年3月,塔塔科技(Tata Technologies)确认曾在去年遭受勒索软件组织“猎手国际”(Hunters International)的攻击,1.4TB工程数据被窃取,涉及航空发动机热障涂层工艺参数等超过 73 万份文件。 X…...
工业控制核心引擎高性能MCU——MM32F5370
RAMSUN提供的MM32F5370搭载180MHz Arm China Star-MC1处理器,集成DSP、FPU与三角函数加速单元(CORDIC),轻松应对复杂算法需求。其技术亮点包括: 超高精度PWM:8通道208ps级高精度PWM输出,满足储能…...

Maven---配置本地仓库
目录 5. 5.1在Maven路径下新建文件夹用于本地仓库存储 5.2 复制本地仓库路径 5.3 找到配置文件路径,使用VSCode方式打开 5.4 新增一行代码 5.5 复制本地仓库路径,设置存储路径 5.1在Maven路径下新建文件夹用于本地仓库存储 5.2 复制本地仓库路径 5…...
vue中events选项与$on监听自定义事件他们的区别与不同,以及$emit与$on之间通信和mounted生命周期钩子函数有哪些作用和属性
events 选项确实曾经被用于监听事件,但它主要用于早期版本的 Vue.js(1.x)中,用于组件之间的通信。在 Vue 2.x 中,events 选项已经被废弃,取而代之的是更强大的 $emit 和 $on 方法。 使用$emit来监听自定义…...

【C++ 】智能指针:内存管理的 “自动导航仪”
目录 一、引入 二、智能指针的两大特性: 1、RAII 特点: 好处: 2、行为像指针 三、智能指针起初的缺陷:拷贝问题 四、几种智能指针的介绍。 1、C98出现的智能指针——auto_ptr auto_ptr解决上述拷贝构造的问题:…...

设备制造行业项目管理难点解析,如何有效解决?
在设备制造行业,项目管理是企业运营的核心环节,直接影响项目交付效率、成本控制和盈利能力。然而,由于行业特性复杂、项目周期长、涉及部门多,企业在实际操作中常常面临诸多管理痛点。金众诚工程项目管理系统,依托金蝶…...

浅谈 PAM-2 到 PAM-4 的信令技术演变
通信信令技术演进:从 PAM-2 到 PAM-4 在当今数字化高速发展的时代,数据传输需求呈爆炸式增长,行业对通信带宽的要求愈发严苛。为顺应这一趋势,通信信令技术不断革新,曾经占据主导地位的不归零(NRZÿ…...

Protos-SIP:经典 SIP 协议模糊测试工具!全参数详细教程!Kali Linux教程!
简介 该测试套件的目的是评估会话发起协议 (SIP) 实现的实现级别安全性和稳健性。 Protos-SIP 是一款专为 SIP 协议模糊测试(Fuzzing)设计的工具,最初由 OUSPG(Oulu University Secure Programming Group)开发&#…...

复数三角不等式简介及 MATLAB 演示
复数三角不等式简介及 MATLAB 演示 1. 复数三角不等式简介 复数三角不等式(Complex Triangle Inequality)是复数的一种重要性质,它类似于普通的三角不等式,但适用于复数空间。具体来说,复数三角不等式可以描述复数之…...

【Doris基础】Apache Doris 基本架构深度解析:从存储到查询的完整技术演进
目录 1 引言 2 Doris 架构全景图 2 核心组件技术解析 2.1 Frontend 层(FE) 2.2 Backend 层(BE) 3 数据存储与复制机制 3.1 存储架构演进 3.2 副本复制策略 4 查询处理全流程解析 4.1 查询生命周期 5 高可用设计 5.1 F…...

程序人生-hellohelloo
计算机系统 大作业 题 目 程序人生-Hello’s P2P 专 业 计算机与电子通信 学 号 2023111976 班 级 23L0504 学 生 孙恩旗 指 导 教 师 刘宏伟 计算机科…...

ASP.NET Core SignalR的基本使用
文章目录 前言一、SignalR是什么?在 ASP.NET Core 中的关键特性:SignalR 工作原理简图: 二、使用步骤1.创建ASP.NET Core web Api 项目2.添加 SignalR 包3.创建 SignalR Hub4.配置服务与中间件5.创建控制器(模拟服务器向客户端发送消息)6.创建…...

【C语言】讲解 程序分配的区域(新手)
目录 代码区 数据区 堆区 栈区 常量区 重点比较一下堆区与 栈区 总结: 前言: C语言程序的内存分配区域是理解其运行机制的重要部分。根据提供的多条证据,我们可以总结出C语言程序在运行时主要涉及以下五个关键内存区域: 代…...
【脚本 完全参数化的通用 APT 源配置方案-Debian/Ubuntu】
通过脚本在 Debian/Ubuntu 系统上一键切换 APT 源 如Dockerfile中 使用某个源(比如 aliyun) 假设你的目录结构是: . ├── Dockerfile └── switch-apt-source.shFROM ubuntu:22.04# 把脚本拷贝到镜像中 COPY switch-apt-source.sh /us…...
数据集笔记:SeekWorld
提出了一项新任务:地理定位推理(Geolocation Reasoning) 该任务要求模型在感知视觉信息的同时,推理出图像中视觉语义所隐含的高级逻辑关系,从而确定图像的拍摄地点 TheEighthDay/SeekWorld at main 构建了一个基于规则…...

LeetCode 算 法 实 战 - - - 移 除 链 表 元 素、反 转 链 表
LeetCode 算 法 实 战 - - - 移 除 链 表 元 素、反 转 链 表 第 一 题 - - - 移 除 链 表 元 素方 法 一 - - - 原 地 删 除方 法 二 - - - 双 指 针方 法 三 - - - 尾 插 第 二 题 - - - 反 转 链 表方 法 一 - - - 迭 代方 法 二 - - - 采 用 头 插 创 建 新 链 表 总 结 &a…...
Jenkins实践(10):pipeline构建历史展示包名和各阶段间传递参数
Jenkins实践(10):构建历史展示包名和pipeline各阶段间传递参数 1、构建历史展示包名 参考:https://blog.csdn.net/fen_fen/article/details/148167868 1.1、方法说明 Jenkins版本:Jenkins2.452 通过修改 currentBuild.displayName 和 currentBuild.description 实现: …...
从头认识AI-----循环神经网络(RNN)
前言 前面我们讲了传统的神经网络,如MLP、CNN,这些网络中的输入都被单独处理,没有上下文之间的信息传递机制,这在处理序列数据(如语音、文本、时间序列)时很鸡肋: 如何理解一句话中“前后文”的…...
配置远程无密登陆ubuntu服务器时无法连接问题排查
配置远程无密登陆ubuntu服务器时无法连接问题排查 登陆端排查服务器端登陆排查 登陆端排查 ssh -v 用户名Ubuntu服务器IP可能日志输出 debug1: Authentications that can continue: publickey,password服务器端登陆排查 sudo tail -f /var/log/auth.log可能日志输出 Authen…...
5.31 数学复习笔记 22
前面的笔记,全部写成一段,有点难以阅读。现在改进一下排版。另外,写笔记实际上就是图一个放松呢,关键还是在于练习。 目前的计划是,把讲义上面的高数例题搞清楚之后,大量刷练习册上面的题。感觉不做几本练…...

kafka学习笔记(三、消费者Consumer使用教程——使用实例及及核心流程源码讲解)
1.核心概念与架构 1.1.消费者与消费者组 Kafka消费者是订阅主题(Topic)并拉取消息的客户端实例,其核心逻辑通过KafkaConsumer类实现。消费者组(Consumer Group)是由多个逻辑关联的消费者组成的集合。 核心规则 同一…...

鸿蒙 Form Kit(卡片开发服务)
Form Kit(卡片开发服务) 鸿蒙应用中,Form / Card / Widget 都翻译为“卡片” Form Kit(卡片开发服务)提供一种界面展示形式,可以将应用的重要信息或操作前置到服务卡片,以达到服务直达、减少跳转…...

算力卡上部署OCR文本识别服务与测试
使用modelscope上的图像文本行检测和文本识别模型进行本地部署并转为API服务。 本地部署时把代码中的检测和识别模型路径改为本地模型的路径。 关于模型和代码原理可以参见modelscope上这两个模型相关的页面: iic/cv_resnet18_ocr-detection-db-line-level_damo iic…...

KWIC—Implicit Invocation
KWIC—Implicit Invocation ✏️ KWIC—Implicit Invocation 文章目录 KWIC—Implicit Invocation📝KWIC—Implicit Invocation🧩KWIC🧩核心组件🧩ImplementationScheme⚖️ 隐式调用 vs 显式调用对比 🌟 总结 &#x…...

Visual Studio 2022 发布独立的 exe 文件
我们在用 Visual Studio 2022 写好一个 exe 程序之后,如果想把这个拿到其他地方运行,需要把 exe 所在的文件夹一起拿过去。 编译出来的 exe 文件需要其他几个文件一同放在同一目录才能运行,原因在于默认情况下,Visual Studio 是把…...

11.4java语言执行浅析4
编译成字节码(.class 文件) 使用 javac 命令将源代码编译为 Java 字节码(bytecode) 它不是机器码,而是 JVM 能理解的中间语言(字节码),具有平台无关性。 编译过程简要࿱…...