【C#进阶】高级面向对象特性_2024-06-22
一、概念
1. 高级面向对象特性
面向对象编程(OOP)是一种编程范式,它使用“对象”来设计软件。这些对象可以包含数据和行为。高级面向对象特性包括:
- 封装:把数据和操作这些数据的代码打包在一起,不让外部直接访问数据,而是通过方法来操作。
- 继承:允许新创建的类(子类)继承现有类(父类)的属性和方法,但可以添加或重写自己的特性。
- 多态:让不同的对象可以对同一消息做出响应,但具体的行为会根据对象的实际类型而有所不同。
2. 接口和抽象类
- 接口(Interface):可以想象成一个“合同”,它规定了类必须实现的方法和属性,但不提供实现细节。任何类都可以实现多个接口。
- 抽象类(Abstract Class):是一个不能被实例化的类,通常包含一些抽象方法(没有实现的方法),子类必须提供这些方法的具体实现。
3. 委托和事件
- 委托(Delegate):可以看作是一个类型安全的函数指针,可以指向任何符合其签名的方法。
- 事件(Event):是一种特殊的多播委托,用于对象之间的通信。一个对象可以“发布”事件,其他对象可以“订阅”这些事件。
4. 异常高级处理
- 异常是程序运行时出现的错误。C#提供了一套机制来捕获和处理这些错误。
- 你可以使用
try
块来包围可能出错的代码,catch
块来捕获并处理特定的异常,finally
块来执行无论是否发生异常都需要执行的代码。
5. 反射和特性
- 反射(Reflection):允许程序在运行时查询和操作对象的类型信息,比如获取类的属性、方法等。
- 特性(Attribute):是一种标记,可以附加到代码元素上(比如类、方法),用来提供元数据信息。这些信息可以在运行时通过反射读取。
二、实例展示
1. 接口和抽象类
接口(Interface):想象一下,你经营一家餐厅,规定所有员工都必须会“服务顾客”。不论他们是厨师、服务员还是收银员,这个“服务顾客”的能力就像是一个约定。在C#里,我们用接口来定义这样的约定。接口只说你“必须”做什么,但具体怎么做,它不管。
// 定义一个接口 ICustomerService
public interface ICustomerService
{
// 规定所有实现这个接口的类,都必须有 ServeCustomer 这个方法
void ServeCustomer();
}
// 厨师类实现接口
public class Chef : ICustomerService
{
public void ServeCustomer()
{
Console.WriteLine("厨师微笑着给顾客上菜。");
}
}
抽象类(Abstract Class):如果说接口是严格的“你必须做”,那么抽象类就是“我给你一些基础,你再根据需要扩展”。抽象类可以有具体实现的方法,也可以有抽象方法(就是只有方法名,没有具体实现)。
public abstract class Employee
{
// 具体实现的方法
public void Greet()
{
Console.WriteLine("欢迎光临!");
}
// 抽象方法,留给子类去实现
public abstract void DoWork();
}
public class Waiter : Employee
{
public override void DoWork()
{
Console.WriteLine("服务员忙着点单。");
}
}
2. 委托和事件
委托(Delegate):想象你在餐厅里放了一个意见箱,任何人都可以往里面投递反馈,而你定期检查并处理这些反馈。委托就像这个意见箱,它允许你定义一个方法的类型,然后你可以将多个方法像纸条一样丢进去,最后一起或单独调用它们。
public delegate void FeedbackHandler(string feedback);
public class Restaurant
{
public event FeedbackHandler NewFeedback;
public void ReceiveFeedback(string feedback)
{
Console.WriteLine($"收到反馈: {feedback}");
// 如果有注册的方法,就调用它们
NewFeedback?.Invoke(feedback);
}
}
// 使用委托
Restaurant myRestaurant = new Restaurant();
myRestaurant.NewFeedback += DisplayFeedbackOnScreen;
myRestaurant.NewFeedback += LogFeedbackToFile;
// 显示反馈到屏幕上
private static void DisplayFeedbackOnScreen(string feedback)
{
Console.WriteLine($"屏幕上显示: {feedback}");
}
// 将反馈记录到文件
private static void LogFeedbackToFile(string feedback)
{
// 实现日志记录逻辑
Console.WriteLine($"记录到文件: {feedback}");
}
3. 异常高级处理
异常处理就像餐厅里应对突发事件的预案。如果厨房突然没煤气了(异常),你不希望整个餐厅停止服务,而是要优雅地告诉顾客“稍等,我们正在解决”,同时后台紧急处理问题。
try
{
// 尝试执行可能会出错的代码,比如打开一个不存在的文件
using (StreamReader file = new StreamReader("不存在的文件.txt"))
{
string content = file.ReadToEnd();
}
}
catch (FileNotFoundException)
{
// 如果找不到文件,友好提示用户
Console.WriteLine("抱歉,文件找不到了。");
}
finally
{
// 不管是否出错,这里都会执行,用于清理资源
Console.WriteLine("操作完成,清理中...");
}
4. 反射和特性
反射(Reflection):就像拥有一面神奇的镜子,能让你在程序运行时查看和操作类、属性、方法等信息。比如,你的餐厅需要根据菜单动态调整价格,反射就能帮助你获取菜单上所有菜品的信息,并修改它们的价格。
using System.Reflection;
public class Dish
{
public string Name { get; set; }
public decimal Price { get; set; }
}
public class Program
{
public static void AdjustPrices(object obj)
{
Type type = obj.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo prop in properties)
{
if (prop.Name == "Price")
{
prop.SetValue(obj, (decimal)prop.GetValue(obj) * 1.1m); // 调整价格
}
}
}
public static void Main(string[] args)
{
Dish spicyChicken = new Dish { Name = "辣子鸡", Price = 50.0m };
AdjustPrices(spicyChicken);
Console.WriteLine($"调整后辣子鸡的价格: {spicyChicken.Price}");
}
}
特性(Attribute):特性就像给餐厅的菜品贴上标签,比如“新品”、“热销”等。在代码里,特性是一种元数据,用来给类、方法等添加额外的信息。
[MenuCategory("热销")]
public class SpicyChicken : Dish
{
// 省略具体实现...
}
// 获取特性
var type = typeof(SpicyChicken);
var categoryAttr = (MenuCategoryAttribute)Attribute.GetCustomAttribute(type, typeof(MenuCategoryAttribute));
Console.WriteLine($"菜品分类: {categoryAttr.Category}");
如果你直接输入上面这段代码你会发现报错缺少引用,之所以报错,是因为MenuCategory
这个特性类没有被定义。特性(Attribute)在C#中是一个特殊的类,通常继承自System.Attribute
基类。在使用自定义特性前,你需要先定义它。下面来补充完整这部分内容:
首先,定义一个名为MenuCategoryAttribute
的特性类。这个类需要继承自System.Attribute
,并且你可以为其定义属性,比如这里的Category
。
using System;
// 定义MenuCategory特性类
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] // 控制特性使用的范围和是否可重复
public class MenuCategoryAttribute : Attribute
{
public string Category { get; }
public MenuCategoryAttribute(string category)
{
Category = category;
}
}
接下来,你之前的代码就可以正常工作了,因为现在MenuCategory
特性已经被正确定义了。
[MenuCategory("热销")] // 现在这部分应该不会报错了
public class SpicyChicken : Dish
{
// 省略具体实现...
}
// 获取特性
var type = typeof(SpicyChicken);
var categoryAttr = (MenuCategoryAttribute)Attribute.GetCustomAttribute(type, typeof(MenuCategoryAttribute));
if (categoryAttr != null)
{
Console.WriteLine($"菜品分类: {categoryAttr.Category}");
}
else
{
Console.WriteLine("未找到MenuCategory特性");
}
记得包含对自定义特性类所在命名空间的引用,如果特性类不在同一命名空间下的话。这样,你就能成功地定义和使用自定义特性了。
三、总结
-
接口(Interface):
- 定义了一组方法签名,任何实现该接口的类都需要提供这些方法的具体实现。
- 用于确保不同类间的一致性或共享特定功能集的约定。
- 接口中所有成员默认为公有,且只能定义方法、属性、索引器和事件。
-
抽象类(Abstract Class):
- 可以包含抽象方法(无具体实现)和具体实现的方法。
- 不能直接实例化,用于作为其他类的基类,强制子类实现特定方法。
- 提供了一定程度的代码复用和结构规划。
-
委托(Delegate):
- 类似于函数指针,用于封装方法的引用,支持多播(绑定多个方法)。
- 在事件处理、回调函数等场景中广泛使用。
- C#中的Lambda表达式和Action、Func泛型委托进一步简化了委托的使用。
-
事件(Event):
- 基于委托的一种通信机制,用于在类内部状态改变时通知外部代码。
- 通常包含一个add和remove访问器,用于管理委托链表。
- 事件发布者触发事件,事件订阅者通过提供事件处理方法响应。
-
异常处理:
- 用于处理程序运行时可能出现的错误情况,保证程序健壮性。
- 包括try-catch-finally结构,用于捕获并处理异常,以及finally块确保清理资源。
- 可以自定义异常类,更精确地描述错误类型。
-
反射(Reflection):
- 在运行时动态获取类型信息(如类名、属性、方法等)并操作这些信息的技术。
- 用于实现诸如插件系统、序列化、动态类型创建等高级功能。
- 性能开销相对较高,应谨慎使用。
-
特性(Attribute):
- 为程序元素(如类、方法、属性)提供元数据的途径。
- 特性以[@AttributeName]形式应用,可通过反射读取。
- 用于标记、配置或给编译器和运行时提供指令,增强代码的描述性和可操作性。
每个知识点都是C#面向对象编程中不可或缺的一部分,掌握它们能让你编写出更灵活、高效、易于维护的代码。