青少年编程与数学 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++ 依赖程序员手动管理内存分配和释放,使用
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# 面向对象编程的核心概念和特性,包括类的定义、对象的创建和使用、继承、接口、多态、构造函数、析构函数、静态成员、抽象类、密封类、属性、索引器和事件等。通过详细的代码示例,展示了如何在实际开发中应用这些特性。类是面向对象编程的基础,通过类可以创建对象实例,实现封装、继承和多态等特性。接口和抽象类用于规范行为,实现多态和代码复用。静态成员和密封类提供了全局访问点和不可继承的特性。多态通过方法重写和接口实现,使得程序更加灵活和可扩展。通过综合示例代码,读者可以更好地理解和掌握这些概念,提升面向对象编程的能力。