【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特性");
}

记得包含对自定义特性类所在命名空间的引用,如果特性类不在同一命名空间下的话。这样,你就能成功地定义和使用自定义特性了。

三、总结

  1. 接口(Interface)

    • 定义了一组方法签名,任何实现该接口的类都需要提供这些方法的具体实现。
    • 用于确保不同类间的一致性或共享特定功能集的约定。
    • 接口中所有成员默认为公有,且只能定义方法、属性、索引器和事件。
  2. 抽象类(Abstract Class)

    • 可以包含抽象方法(无具体实现)和具体实现的方法。
    • 不能直接实例化,用于作为其他类的基类,强制子类实现特定方法。
    • 提供了一定程度的代码复用和结构规划。
  3. 委托(Delegate)

    • 类似于函数指针,用于封装方法的引用,支持多播(绑定多个方法)。
    • 在事件处理、回调函数等场景中广泛使用。
    • C#中的Lambda表达式和Action、Func泛型委托进一步简化了委托的使用。
  4. 事件(Event)

    • 基于委托的一种通信机制,用于在类内部状态改变时通知外部代码。
    • 通常包含一个add和remove访问器,用于管理委托链表。
    • 事件发布者触发事件,事件订阅者通过提供事件处理方法响应。
  5. 异常处理

    • 用于处理程序运行时可能出现的错误情况,保证程序健壮性。
    • 包括try-catch-finally结构,用于捕获并处理异常,以及finally块确保清理资源。
    • 可以自定义异常类,更精确地描述错误类型。
  6. 反射(Reflection)

    • 在运行时动态获取类型信息(如类名、属性、方法等)并操作这些信息的技术。
    • 用于实现诸如插件系统、序列化、动态类型创建等高级功能。
    • 性能开销相对较高,应谨慎使用。
  7. 特性(Attribute)

    • 为程序元素(如类、方法、属性)提供元数据的途径。
    • 特性以[@AttributeName]形式应用,可通过反射读取。
    • 用于标记、配置或给编译器和运行时提供指令,增强代码的描述性和可操作性。

每个知识点都是C#面向对象编程中不可或缺的一部分,掌握它们能让你编写出更灵活、高效、易于维护的代码。

posted @ 2024-06-22 11:31  StarYou  阅读(34)  评论(0编辑  收藏  举报