C# 零基础到精通教程 - 第九章:面向对象编程(高级)——接口、委托与事件

9.1 接口(Interface)

9.1.1 为什么需要接口?

问题:C# 不支持多继承

csharp

复制代码
class Bird { public void Fly() { } }
class Fish { public void Swim() { } }

// 一个类只能继承一个父类
// class Duck : Bird, Fish  // ❌ 错误!不能多继承

解决方案:接口

csharp

复制代码
interface IFlyable
{
    void Fly();
}

interface ISwimmable
{
    void Swim();
}

class Duck : IFlyable, ISwimmable  // 可以实现多个接口
{
    public void Fly()
    {
        Console.WriteLine("鸭子正在飞");
    }
    
    public void Swim()
    {
        Console.WriteLine("鸭子正在游泳");
    }
}

9.1.2 接口的基本语法

接口 = 一组方法、属性、事件的声明,没有实现

csharp

复制代码
// 定义接口(通常以 I 开头)
interface IAnimal
{
    // 方法(没有方法体)
    void MakeSound();
    
    // 属性(只有声明,没有实现)
    string Name { get; set; }
    
    // 只读属性
    int Age { get; }
}

// 实现接口
class Dog : IAnimal
{
    public string Name { get; set; }
    
    public int Age => 3;  // 只读属性实现
    
    public void MakeSound()
    {
        Console.WriteLine($"{Name}:汪汪汪");
    }
}

// 使用
Dog dog = new Dog();
dog.Name = "旺财";
dog.MakeSound();  // 旺财:汪汪汪

9.1.3 接口 vs 抽象类

特性 接口 抽象类
关键字 interface abstract class
多重继承 可以实现多个 只能继承一个
字段 不能有字段(C# 8.0 前) 可以有字段
构造函数 不能有 可以有
实现代码 C# 8.0 前不能有 可以有
访问修饰符 默认 public 可以各种修饰符
适用场景 定义"能力/契约" 定义"是什么"

csharp

复制代码
// 抽象类:表示"是什么"
abstract class Animal
{
    public string Name { get; set; }
    public abstract void MakeSound();
}

// 接口:表示"能做什么"
interface IFlyable
{
    void Fly();
}

interface ISwimmable
{
    void Swim();
}

// 一个类继承一个抽象类,实现多个接口
class Duck : Animal, IFlyable, ISwimmable
{
    public override void MakeSound()
    {
        Console.WriteLine("嘎嘎嘎");
    }
    
    public void Fly()
    {
        Console.WriteLine("鸭子飞起来了");
    }
    
    public void Swim()
    {
        Console.WriteLine("鸭子游起来了");
    }
}

9.1.4 接口的多种实现方式

csharp

复制代码
interface IWorker
{
    void Work();
    string Name { get; set; }
}

// 方式1:普通实现
class Engineer : IWorker
{
    public string Name { get; set; }
    
    public void Work()
    {
        Console.WriteLine($"{Name}正在写代码");
    }
}

// 方式2:显式接口实现(解决方法名冲突)
interface IFileOperation
{
    void Save();
}

interface IDatabaseOperation
{
    void Save();
}

class DataManager : IFileOperation, IDatabaseOperation
{
    // 显式实现接口方法
    void IFileOperation.Save()
    {
        Console.WriteLine("保存到文件");
    }
    
    void IDatabaseOperation.Save()
    {
        Console.WriteLine("保存到数据库");
    }
    
    // 普通方法
    public void Save()
    {
        Console.WriteLine("普通保存");
    }
}

// 使用
DataManager dm = new DataManager();
dm.Save();                    // 普通保存
((IFileOperation)dm).Save();  // 保存到文件
((IDatabaseOperation)dm).Save(); // 保存到数据库

9.1.5 接口的继承

接口可以继承其他接口

csharp

复制代码
interface IMovable
{
    void Move();
}

interface IAttackable
{
    void Attack();
}

// 继承多个接口
interface ICharacter : IMovable, IAttackable
{
    void Die();
}

class Player : ICharacter
{
    public void Move()
    {
        Console.WriteLine("玩家移动");
    }
    
    public void Attack()
    {
        Console.WriteLine("玩家攻击");
    }
    
    public void Die()
    {
        Console.WriteLine("玩家死亡");
    }
}

9.1.6 接口的多态

csharp

复制代码
interface IAnimal
{
    void MakeSound();
}

class Dog : IAnimal
{
    public void MakeSound() => Console.WriteLine("汪汪");
}

class Cat : IAnimal
{
    public void MakeSound() => Console.WriteLine("喵喵");
}

class Cow : IAnimal
{
    public void MakeSound() => Console.WriteLine("哞哞");
}

// 使用接口多态
IAnimal[] animals = new IAnimal[]
{
    new Dog(),
    new Cat(),
    new Cow()
};

foreach (IAnimal animal in animals)
{
    animal.MakeSound();
}

9.1.7 接口的默认实现(C# 8.0+)

csharp

复制代码
interface ILogger
{
    void Log(string message);
    
    // 默认实现(C# 8.0)
    void LogError(string error)
    {
        Log($"ERROR: {error}");
    }
    
    // 静态方法
    static void LogInfo(string info)
    {
        Console.WriteLine($"INFO: {info}");
    }
}

class ConsoleLogger : ILogger
{
    public void Log(string message)
    {
        Console.WriteLine($"[LOG] {message}");
    }
    // 可以不实现 LogError,使用默认实现
}

// 使用
ConsoleLogger logger = new ConsoleLogger();
logger.Log("Hello");
((ILogger)logger).LogError("出错了");  // 需要通过接口调用
ILogger.LogInfo("信息");  // 静态方法通过接口调用

9.2 委托(Delegate)

9.2.1 为什么需要委托?

委托 = 方法的类型。可以把方法当作参数传递、当作变量存储。

csharp

复制代码
// 没有委托:代码重复
class Calculator
{
    public void Add(int a, int b)
    {
        Console.WriteLine($"结果:{a + b}");
    }
    
    public void Subtract(int a, int b)
    {
        Console.WriteLine($"结果:{a - b}");
    }
}

// 需要写两个几乎一样的方法

有委托:方法作为参数

csharp

复制代码
// 定义委托类型
public delegate int MathOperation(int a, int b);

class Calculator
{
    // 方法实现
    public static int Add(int a, int b) => a + b;
    public static int Subtract(int a, int b) => a - b;
    public static int Multiply(int a, int b) => a * b;
    
    // 这个方法接受委托作为参数
    public static void Calculate(int a, int b, MathOperation operation)
    {
        int result = operation(a, b);
        Console.WriteLine($"{a} 和 {b} 的计算结果:{result}");
    }
}

// 使用
Calculator.Calculate(10, 5, Calculator.Add);      // 15
Calculator.Calculate(10, 5, Calculator.Subtract); // 5
Calculator.Calculate(10, 5, Calculator.Multiply); // 50

9.2.2 委托的基本语法

csharp

复制代码
// 1. 声明委托(定义方法的签名)
delegate 返回值类型 委托名(参数列表);

// 2. 创建委托实例
委托名 变量名 = 方法名;

// 3. 调用委托
变量名(参数);

// 完整示例
public delegate void GreetingDelegate(string name);

class Program
{
    static void SayHello(string name)
    {
        Console.WriteLine($"你好,{name}!");
    }
    
    static void SayGoodbye(string name)
    {
        Console.WriteLine($"再见,{name}!");
    }
    
    static void Main()
    {
        GreetingDelegate greet = SayHello;
        greet("张三");  // 你好,张三!
        
        greet = SayGoodbye;
        greet("张三");  // 再见,张三!
    }
}

9.2.3 多播委托(Multicast Delegate)

一个委托可以绑定多个方法,调用时会依次执行所有绑定的方法

csharp

复制代码
delegate void NotifyDelegate(string message);

class Program
{
    static void ConsoleNotify(string msg)
    {
        Console.WriteLine($"[控制台] {msg}");
    }
    
    static void FileNotify(string msg)
    {
        // 模拟写入文件
        Console.WriteLine($"[文件] {msg}已写入");
    }
    
    static void EmailNotify(string msg)
    {
        Console.WriteLine($"[邮件] {msg}已发送");
    }
    
    static void Main()
    {
        NotifyDelegate notifier = null;
        
        // 添加方法
        notifier += ConsoleNotify;
        notifier += FileNotify;
        notifier += EmailNotify;
        
        // 调用所有方法
        notifier("系统更新完成");
        // 输出:
        // [控制台] 系统更新完成
        // [文件] 系统更新完成已写入
        // [邮件] 系统更新完成已发送
        
        // 移除方法
        notifier -= FileNotify;
        notifier("第二次通知");
        // 输出:[控制台] 第二次通知   [邮件] 第二次通知已发送
        
        // 获取委托的调用列表
        Delegate[] invocationList = notifier.GetInvocationList();
        Console.WriteLine($"绑定了 {invocationList.Length} 个方法");
    }
}

9.2.4 内置委托类型

C# 提供了常用的内置委托,不需要自己声明

委托类型 签名 说明
Action void Action() 无参数,无返回值
Action<T> void Action<T>(T obj) 1个参数,无返回值
Action<T1,T2> void Action<T1,T2>(T1 t1, T2 t2) 2个参数,无返回值
Func<TResult> TResult Func() 无参数,有返回值
Func<T,TResult> TResult Func(T arg) 1个参数,有返回值
Predicate<T> bool Predicate(T obj) 1个参数,返回 bool

csharp

复制代码
// Action:无返回值
Action sayHello = () => Console.WriteLine("Hello");
sayHello();  // Hello

Action<string> greet = name => Console.WriteLine($"Hello, {name}");
greet("张三");  // Hello, 张三

// Func:有返回值
Func<int, int, int> add = (a, b) => a + b;
int result = add(5, 3);  // 8

Func<int, bool> isEven = n => n % 2 == 0;
bool even = isEven(4);  // true

// Predicate:返回 bool
Predicate<int> isPositive = n => n > 0;
bool positive = isPositive(10);  // true

// 实际应用:数组筛选
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Predicate<int> isEvenPredicate = n => n % 2 == 0;
int[] evens = Array.FindAll(numbers, isEvenPredicate);
Console.WriteLine($"偶数:{string.Join(", ", evens)}");

9.2.5 委托的实用场景

csharp

复制代码
// 场景1:排序自定义
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    
    public override string ToString() => $"{Name}({Age})";
}

class PersonSorter
{
    public static void Sort(Person[] people, Func<Person, Person, int> compare)
    {
        for (int i = 0; i < people.Length - 1; i++)
        {
            for (int j = 0; j < people.Length - i - 1; j++)
            {
                if (compare(people[j], people[j + 1]) > 0)
                {
                    Person temp = people[j];
                    people[j] = people[j + 1];
                    people[j + 1] = temp;
                }
            }
        }
    }
}

// 使用
Person[] people = new Person[]
{
    new Person { Name = "张三", Age = 25 },
    new Person { Name = "李四", Age = 20 },
    new Person { Name = "王五", Age = 30 }
};

// 按年龄排序
PersonSorter.Sort(people, (p1, p2) => p1.Age.CompareTo(p2.Age));
Console.WriteLine("按年龄排序:" + string.Join(", ", people));

// 按姓名排序
PersonSorter.Sort(people, (p1, p2) => p1.Name.CompareTo(p2.Name));
Console.WriteLine("按姓名排序:" + string.Join(", ", people));

csharp

复制代码
// 场景2:回调函数
class Downloader
{
    public delegate void DownloadProgressDelegate(int percent);
    public delegate void DownloadCompleteDelegate(string fileName);
    
    public void Download(string url, 
                         DownloadProgressDelegate onProgress,
                         DownloadCompleteDelegate onComplete)
    {
        Console.WriteLine($"开始下载:{url}");
        
        for (int i = 0; i <= 100; i += 20)
        {
            Thread.Sleep(500);  // 模拟下载
            onProgress?.Invoke(i);  // 触发进度回调
        }
        
        string fileName = "downloaded_file.zip";
        onComplete?.Invoke(fileName);
    }
}

// 使用
Downloader downloader = new Downloader();
downloader.Download("http://example.com/file.zip",
    progress => Console.WriteLine($"下载进度:{progress}%"),
    file => Console.WriteLine($"下载完成:{file}")
);

9.3 事件(Event)

9.3.1 什么是事件?

事件 = 基于委托的发布/订阅机制。事件是类的成员,用于在某个动作发生时通知其他对象。

csharp

复制代码
// 事件的基本结构
public event 委托类型 事件名;

// 完整示例:按钮点击事件
class Button
{
    // 声明事件(基于 Action 委托)
    public event Action OnClick;
    
    public void Click()
    {
        Console.WriteLine("按钮被点击");
        // 触发事件
        OnClick?.Invoke();  // ?. 表示如果有订阅者才调用
    }
}

class Program
{
    static void Main()
    {
        Button btn = new Button();
        
        // 订阅事件
        btn.OnClick += ButtonClicked;
        btn.OnClick += () => Console.WriteLine("按钮点击后的额外操作");
        
        btn.Click();  // 触发事件
    }
    
    static void ButtonClicked()
    {
        Console.WriteLine("按钮点击事件被触发!");
    }
}

9.3.2 事件的完整语法(符合 .NET 规范)

csharp

复制代码
// .NET 标准事件模式:使用 EventHandler 委托
using System;

// 自定义事件参数(携带额外信息)
class AlarmEventArgs : EventArgs
{
    public string Message { get; set; }
    public DateTime Time { get; set; }
    
    public AlarmEventArgs(string message)
    {
        Message = message;
        Time = DateTime.Now;
    }
}

class AlarmClock
{
    // 标准事件声明
    public event EventHandler<AlarmEventArgs> Alarm;
    
    // 受保护的触发方法(供子类重写)
    protected virtual void OnAlarm(AlarmEventArgs e)
    {
        Alarm?.Invoke(this, e);
    }
    
    public void SetAlarm(DateTime alarmTime)
    {
        Console.WriteLine($"闹钟已设置为:{alarmTime:HH:mm:ss}");
        
        // 模拟等待
        while (DateTime.Now < alarmTime)
        {
            Thread.Sleep(1000);
        }
        
        // 触发闹钟事件
        OnAlarm(new AlarmEventArgs("起床时间到了!"));
    }
}

class Program
{
    static void Main()
    {
        AlarmClock clock = new AlarmClock();
        
        // 订阅事件
        clock.Alarm += Clock_Alarm;
        clock.Alarm += (sender, e) => Console.WriteLine($"通知:{e.Message} 时间:{e.Time}");
        
        // 设置闹钟(5秒后)
        clock.SetAlarm(DateTime.Now.AddSeconds(5));
    }
    
    static void Clock_Alarm(object sender, AlarmEventArgs e)
    {
        Console.WriteLine($"闹钟响了!{e.Message}");
        Console.WriteLine($"当前时间:{e.Time:HH:mm:ss}");
    }
}

9.3.3 自定义事件委托

csharp

复制代码
// 自定义事件委托
public delegate void TemperatureChangedEventHandler(double oldTemp, double newTemp);

class Thermostat
{
    private double temperature;
    private double threshold;
    
    // 使用自定义委托的事件
    public event TemperatureChangedEventHandler TemperatureChanged;
    
    public double Temperature
    {
        get { return temperature; }
        set
        {
            if (value != temperature)
            {
                double oldTemp = temperature;
                temperature = value;
                
                // 超过阈值时才触发事件
                if (Math.Abs(oldTemp - value) >= threshold)
                {
                    OnTemperatureChanged(oldTemp, value);
                }
            }
        }
    }
    
    public Thermostat(double threshold = 1.0)
    {
        this.threshold = threshold;
    }
    
    protected virtual void OnTemperatureChanged(double oldTemp, double newTemp)
    {
        TemperatureChanged?.Invoke(oldTemp, newTemp);
    }
}

class Program
{
    static void Main()
    {
        Thermostat thermostat = new Thermostat(0.5);
        
        thermostat.TemperatureChanged += OnTemperatureChanged;
        thermostat.TemperatureChanged += (oldT, newT) => 
            Console.WriteLine($"温度变化了 {Math.Abs(oldT - newT):F1} 度");
        
        thermostat.Temperature = 20.0;
        thermostat.Temperature = 20.3;  // 变化0.3,低于阈值
        thermostat.Temperature = 21.0;  // 变化0.7,触发事件
    }
    
    static void OnTemperatureChanged(double oldTemp, double newTemp)
    {
        Console.WriteLine($"温度从 {oldTemp}°C 变为 {newTemp}°C");
        
        if (newTemp > 30)
            Console.WriteLine("⚠️ 警告:温度过高!");
        else if (newTemp < 10)
            Console.WriteLine("⚠️ 警告:温度过低!");
    }
}

9.3.4 事件 vs 委托

特性 事件 委托
可以外部调用 ❌(只能类内部触发)
可以外部赋值 ❌(只能用 +=/-=) ✅(可以用 =)
可以外部获取调用列表
适用场景 通知发生的变化 回调、策略模式
封装性

csharp

复制代码
class Demo
{
    // 委托字段:可以随意操作
    public Action DelegateField;
    
    // 事件:受限制的操作
    public event Action EventField;
    
    public void Test()
    {
        // 类内部:两者都可以触发
        DelegateField?.Invoke();
        EventField?.Invoke();
    }
}

// 外部使用
Demo demo = new Demo();
demo.DelegateField = () => Console.WriteLine("委托赋值");  // ✅
demo.DelegateField += () => Console.WriteLine("委托添加");  // ✅
demo.DelegateField?.Invoke();  // ✅ 可以外部调用

demo.EventField += () => Console.WriteLine("事件添加");  // ✅
demo.EventField = () => Console.WriteLine("事件赋值");  // ❌ 错误!
demo.EventField?.Invoke();  // ❌ 错误!不能外部触发

9.3.5 事件的实用场景

csharp

复制代码
// 场景:股票价格监控
class Stock
{
    public string Symbol { get; set; }
    private decimal price;
    
    public event EventHandler<PriceChangedEventArgs> PriceChanged;
    
    public decimal Price
    {
        get { return price; }
        set
        {
            if (value != price)
            {
                decimal oldPrice = price;
                price = value;
                OnPriceChanged(oldPrice, value);
            }
        }
    }
    
    public Stock(string symbol, decimal initialPrice)
    {
        Symbol = symbol;
        price = initialPrice;
    }
    
    protected virtual void OnPriceChanged(decimal oldPrice, decimal newPrice)
    {
        PriceChanged?.Invoke(this, new PriceChangedEventArgs(oldPrice, newPrice));
    }
}

class PriceChangedEventArgs : EventArgs
{
    public decimal OldPrice { get; }
    public decimal NewPrice { get; }
    public decimal ChangePercent => (NewPrice - OldPrice) / OldPrice * 100;
    
    public PriceChangedEventArgs(decimal oldPrice, decimal newPrice)
    {
        OldPrice = oldPrice;
        NewPrice = newPrice;
    }
}

class Investor
{
    public string Name { get; set; }
    
    public void Subscribe(Stock stock)
    {
        stock.PriceChanged += OnStockPriceChanged;
    }
    
    private void OnStockPriceChanged(object sender, PriceChangedEventArgs e)
    {
        Stock stock = (Stock)sender;
        Console.WriteLine($"[{Name}] {stock.Symbol} 价格变化:" +
                         $"{e.OldPrice:C} → {e.NewPrice:C} " +
                         $"({e.ChangePercent:+0.00;-0.00}%)");
        
        if (e.ChangePercent <= -5)
            Console.WriteLine($"  → {Name} 决定卖出!");
        else if (e.ChangePercent >= 5)
            Console.WriteLine($"  → {Name} 决定买入!");
    }
}

class Program
{
    static void Main()
    {
        Stock tsla = new Stock("TSLA", 200.00m);
        
        Investor alice = new Investor { Name = "Alice" };
        Investor bob = new Investor { Name = "Bob" };
        
        alice.Subscribe(tsla);
        bob.Subscribe(tsla);
        
        // 模拟价格变化
        tsla.Price = 210.00m;  // +5%
        tsla.Price = 195.00m;  // -7.14%
        tsla.Price = 190.00m;  // -2.56%
    }
}

9.4 Lambda 表达式

9.4.1 什么是 Lambda?

Lambda 表达式 = 匿名方法的简洁写法

csharp

复制代码
// 传统委托写法
Func<int, int, int> add1 = delegate(int a, int b) { return a + b; };

// Lambda 表达式写法
Func<int, int, int> add2 = (int a, int b) => { return a + b; };

// 更简洁的 Lambda
Func<int, int, int> add3 = (a, b) => a + b;

// 最简洁
Func<int, int, int> add4 = (a, b) => a + b;

// 使用
int result = add4(5, 3);  // 8

9.4.2 Lambda 的语法规则

csharp

复制代码
// 基本语法:(参数列表) => 表达式或语句块

// 1. 无参数
Action greet = () => Console.WriteLine("Hello");
greet();

// 2. 一个参数(可以省略括号)
Func<int, int> square = x => x * x;
Console.WriteLine(square(5));  // 25

// 3. 多个参数(需要括号)
Func<int, int, int> add = (x, y) => x + y;

// 4. 多条语句(需要花括号和 return)
Func<int, int, int> multiply = (x, y) =>
{
    Console.WriteLine($"计算 {x} × {y}");
    return x * y;
};

// 5. 无返回值(Action)
Action<string> print = msg => Console.WriteLine(msg);

// 6. 忽略参数(使用 _)
Action<int, int> log = (_, _) => Console.WriteLine("两个参数被忽略");

9.4.3 Lambda 的实用场景

csharp

复制代码
// 场景1:LINQ 查询(第十章会详细讲)
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

// 找出所有偶数
var evens = numbers.Where(n => n % 2 == 0);

// 选择平方
var squares = numbers.Select(n => n * n);

// 排序
var sorted = numbers.OrderByDescending(n => n);

// 聚合
int sum = numbers.Sum(n => n);

// 场景2:List 操作
List<string> names = new List<string> { "张三", "李四", "王五", "赵六" };

// 查找
string found = names.Find(n => n.StartsWith("李"));  // 李四

// 过滤
var filtered = names.Where(n => n.Length == 2);

// 转换
var upperNames = names.Select(n => n.ToUpper());

// 场景3:事件处理
Button btn = new Button();
btn.OnClick += (sender, e) => Console.WriteLine("按钮被点击了");

9.5 综合示例

示例1:媒体播放器系统(接口)

csharp

复制代码
using System;
using System.Collections.Generic;

// 媒体播放接口
interface IMediaPlayer
{
    void Play();
    void Pause();
    void Stop();
    double GetProgress();
    string GetInfo();
}

// 音频播放器
class AudioPlayer : IMediaPlayer
{
    public string Title { get; set; }
    public string Artist { get; set; }
    private bool isPlaying;
    private double progress;
    
    public AudioPlayer(string title, string artist)
    {
        Title = title;
        Artist = artist;
    }
    
    public void Play()
    {
        if (!isPlaying)
        {
            isPlaying = true;
            Console.WriteLine($"▶ 播放音频:{Title} - {Artist}");
        }
    }
    
    public void Pause()
    {
        if (isPlaying)
        {
            isPlaying = false;
            Console.WriteLine($"⏸ 暂停音频:{Title}");
        }
    }
    
    public void Stop()
    {
        isPlaying = false;
        progress = 0;
        Console.WriteLine($"⏹ 停止音频:{Title}");
    }
    
    public double GetProgress()
    {
        if (isPlaying)
        {
            progress += 0.1;
            if (progress > 1) progress = 0;
        }
        return progress;
    }
    
    public string GetInfo() => $"音频文件:{Title},歌手:{Artist}";
}

// 视频播放器
class VideoPlayer : IMediaPlayer
{
    public string Title { get; set; }
    public string Resolution { get; set; }
    private bool isPlaying;
    private double progress;
    
    public VideoPlayer(string title, string resolution)
    {
        Title = title;
        Resolution = resolution;
    }
    
    public void Play()
    {
        if (!isPlaying)
        {
            isPlaying = true;
            Console.WriteLine($"▶ 播放视频:{Title} ({Resolution})");
        }
    }
    
    public void Pause()
    {
        if (isPlaying)
        {
            isPlaying = false;
            Console.WriteLine($"⏸ 暂停视频:{Title}");
        }
    }
    
    public void Stop()
    {
        isPlaying = false;
        progress = 0;
        Console.WriteLine($"⏹ 停止视频:{Title}");
    }
    
    public double GetProgress()
    {
        if (isPlaying)
        {
            progress += 0.05;
            if (progress > 1) progress = 0;
        }
        return progress;
    }
    
    public string GetInfo() => $"视频文件:{Title},分辨率:{Resolution}";
}

// 播放列表
class Playlist
{
    private List<IMediaPlayer> items = new List<IMediaPlayer>();
    private int currentIndex = -1;
    
    public void Add(IMediaPlayer item)
    {
        items.Add(item);
        Console.WriteLine($"已添加:{item.GetInfo()}");
    }
    
    public void Play()
    {
        if (items.Count == 0)
        {
            Console.WriteLine("播放列表为空");
            return;
        }
        
        if (currentIndex == -1)
            currentIndex = 0;
        
        items[currentIndex].Play();
    }
    
    public void Next()
    {
        if (items.Count == 0) return;
        
        if (currentIndex >= 0)
            items[currentIndex].Stop();
        
        currentIndex = (currentIndex + 1) % items.Count;
        items[currentIndex].Play();
    }
    
    public void Prev()
    {
        if (items.Count == 0) return;
        
        if (currentIndex >= 0)
            items[currentIndex].Stop();
        
        currentIndex = (currentIndex - 1 + items.Count) % items.Count;
        items[currentIndex].Play();
    }
    
    public void ShowPlaylist()
    {
        Console.WriteLine("\n=== 播放列表 ===");
        for (int i = 0; i < items.Count; i++)
        {
            string marker = (i == currentIndex) ? "▶ " : "  ";
            Console.WriteLine($"{marker}{i + 1}. {items[i].GetInfo()}");
        }
    }
}

class Program
{
    static void Main()
    {
        Playlist playlist = new Playlist();
        
        playlist.Add(new AudioPlayer("Shape of You", "Ed Sheeran"));
        playlist.Add(new VideoPlayer("Inception", "1080p"));
        playlist.Add(new AudioPlayer("Bohemian Rhapsody", "Queen"));
        
        playlist.ShowPlaylist();
        Console.WriteLine();
        
        playlist.Play();
        playlist.Next();
        playlist.Next();
        playlist.Prev();
    }
}

示例2:温度监控系统(委托与事件)

csharp

复制代码
using System;
using System.Collections.Generic;
using System.Threading;

// 温度数据事件参数
class TemperatureEventArgs : EventArgs
{
    public double Temperature { get; set; }
    public DateTime Time { get; set; }
    
    public TemperatureEventArgs(double temperature)
    {
        Temperature = temperature;
        Time = DateTime.Now;
    }
}

// 温度传感器
class TemperatureSensor
{
    private Random random = new Random();
    private double currentTemp;
    private bool isRunning;
    private Thread monitorThread;
    
    // 事件
    public event EventHandler<TemperatureEventArgs> TemperatureChanged;
    public event EventHandler<TemperatureEventArgs> HighTemperatureAlert;
    public event EventHandler<TemperatureEventArgs> LowTemperatureAlert;
    
    public double CurrentTemp => currentTemp;
    
    public TemperatureSensor()
    {
        currentTemp = 20.0;
    }
    
    public void Start()
    {
        if (isRunning) return;
        isRunning = true;
        
        monitorThread = new Thread(MonitorLoop);
        monitorThread.Start();
        Console.WriteLine("温度传感器已启动");
    }
    
    public void Stop()
    {
        isRunning = false;
        monitorThread?.Join();
        Console.WriteLine("温度传感器已停止");
    }
    
    private void MonitorLoop()
    {
        while (isRunning)
        {
            // 模拟温度变化(-0.5 到 +0.5 度)
            double change = (random.NextDouble() - 0.5);
            currentTemp += change;
            
            // 触发温度变化事件
            OnTemperatureChanged(currentTemp);
            
            // 检查告警
            if (currentTemp > 30)
                OnHighTemperatureAlert(currentTemp);
            else if (currentTemp < 10)
                OnLowTemperatureAlert(currentTemp);
            
            Thread.Sleep(2000);  // 每2秒检测一次
        }
    }
    
    protected virtual void OnTemperatureChanged(double temp)
    {
        TemperatureChanged?.Invoke(this, new TemperatureEventArgs(temp));
    }
    
    protected virtual void OnHighTemperatureAlert(double temp)
    {
        HighTemperatureAlert?.Invoke(this, new TemperatureEventArgs(temp));
    }
    
    protected virtual void OnLowTemperatureAlert(double temp)
    {
        LowTemperatureAlert?.Invoke(this, new TemperatureEventArgs(temp));
    }
}

// 显示器(订阅温度变化)
class Display
{
    public string Name { get; set; }
    
    public Display(string name)
    {
        Name = name;
    }
    
    public void OnTemperatureChanged(object sender, TemperatureEventArgs e)
    {
        Console.WriteLine($"[{Name}] 当前温度:{e.Temperature:F1}°C({e.Time:HH:mm:ss})");
    }
}

// 空调控制器(订阅高温告警)
class AirConditioner
{
    private bool isOn;
    
    public void OnHighTemperature(object sender, TemperatureEventArgs e)
    {
        if (!isOn && e.Temperature > 28)
        {
            isOn = true;
            Console.WriteLine($"❄️ 空调已开启!温度 {e.Temperature:F1}°C");
        }
        else if (isOn && e.Temperature < 24)
        {
            isOn = false;
            Console.WriteLine($"✅ 空调已关闭!温度 {e.Temperature:F1}°C");
        }
    }
}

// 加热器(订阅低温告警)
class Heater
{
    private bool isOn;
    
    public void OnLowTemperature(object sender, TemperatureEventArgs e)
    {
        if (!isOn && e.Temperature < 12)
        {
            isOn = true;
            Console.WriteLine($"🔥 加热器已开启!温度 {e.Temperature:F1}°C");
        }
        else if (isOn && e.Temperature > 16)
        {
            isOn = false;
            Console.WriteLine($"✅ 加热器已关闭!温度 {e.Temperature:F1}°C");
        }
    }
}

// 日志记录器
class Logger
{
    private List<string> logs = new List<string>();
    
    public void Subscribe(TemperatureSensor sensor)
    {
        sensor.TemperatureChanged += Log;
        sensor.HighTemperatureAlert += (s, e) => Log(s, e, "高温告警");
        sensor.LowTemperatureAlert += (s, e) => Log(s, e, "低温告警");
    }
    
    private void Log(object sender, TemperatureEventArgs e, string type = "普通")
    {
        string log = $"[{e.Time:yyyy-MM-dd HH:mm:ss}] {type}:{e.Temperature:F1}°C";
        logs.Add(log);
        Console.WriteLine($"📝 {log}");
    }
    
    public void ShowLogs()
    {
        Console.WriteLine("\n=== 日志记录 ===");
        foreach (string log in logs)
        {
            Console.WriteLine(log);
        }
    }
}

class Program
{
    static void Main()
    {
        Console.WriteLine("=== 温度监控系统启动 ===\n");
        
        // 创建传感器
        TemperatureSensor sensor = new TemperatureSensor();
        
        // 创建显示器
        Display display1 = new Display("客厅显示器");
        Display display2 = new Display("手机App");
        
        // 创建设备
        AirConditioner ac = new AirConditioner();
        Heater heater = new Heater();
        Logger logger = new Logger();
        
        // 订阅事件
        sensor.TemperatureChanged += display1.OnTemperatureChanged;
        sensor.TemperatureChanged += display2.OnTemperatureChanged;
        sensor.HighTemperatureAlert += ac.OnHighTemperature;
        sensor.LowTemperatureAlert += heater.OnLowTemperature;
        logger.Subscribe(sensor);
        
        // 运行30秒
        sensor.Start();
        Thread.Sleep(30000);
        sensor.Stop();
        
        // 显示日志
        logger.ShowLogs();
        
        Console.WriteLine("\n系统已关闭");
    }
}

示例3:任务调度器(Lambda + 委托)

csharp

复制代码
using System;
using System.Collections.Generic;
using System.Threading;

// 任务类
class ScheduledTask
{
    public string Name { get; set; }
    public Action TaskAction { get; set; }
    public DateTime ScheduledTime { get; set; }
    public bool IsRecurring { get; set; }
    public TimeSpan RecurringInterval { get; set; }
    public bool IsCompleted { get; set; }
    
    public ScheduledTask(string name, Action action, DateTime time)
    {
        Name = name;
        TaskAction = action;
        ScheduledTime = time;
        IsRecurring = false;
        IsCompleted = false;
    }
    
    public ScheduledTask(string name, Action action, TimeSpan interval)
    {
        Name = name;
        TaskAction = action;
        ScheduledTime = DateTime.Now.Add(interval);
        IsRecurring = true;
        RecurringInterval = interval;
        IsCompleted = false;
    }
    
    public void Execute()
    {
        Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] 执行任务:{Name}");
        TaskAction?.Invoke();
    }
    
    public void Reschedule()
    {
        if (IsRecurring)
        {
            ScheduledTime = DateTime.Now.Add(RecurringInterval);
            IsCompleted = false;
        }
    }
}

// 任务调度器
class TaskScheduler
{
    private List<ScheduledTask> tasks = new List<ScheduledTask>();
    private bool isRunning;
    private Thread schedulerThread;
    
    // 事件
    public event Action<ScheduledTask> TaskExecuted;
    public event Action<Exception> TaskError;
    
    public void AddTask(ScheduledTask task)
    {
        tasks.Add(task);
        Console.WriteLine($"已添加任务:{task.Name},执行时间:{task.ScheduledTime:HH:mm:ss}");
    }
    
    public void Start()
    {
        if (isRunning) return;
        isRunning = true;
        
        schedulerThread = new Thread(Run);
        schedulerThread.Start();
        Console.WriteLine("任务调度器已启动");
    }
    
    public void Stop()
    {
        isRunning = false;
        schedulerThread?.Join();
        Console.WriteLine("任务调度器已停止");
    }
    
    private void Run()
    {
        while (isRunning)
        {
            DateTime now = DateTime.Now;
            
            foreach (var task in tasks)
            {
                if (!task.IsCompleted && now >= task.ScheduledTime)
                {
                    try
                    {
                        task.Execute();
                        TaskExecuted?.Invoke(task);
                        
                        if (task.IsRecurring)
                        {
                            task.Reschedule();
                        }
                        else
                        {
                            task.IsCompleted = true;
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"任务 {task.Name} 执行失败:{ex.Message}");
                        TaskError?.Invoke(ex);
                    }
                }
            }
            
            Thread.Sleep(1000);  // 每秒检查一次
        }
    }
    
    public void ShowPendingTasks()
    {
        Console.WriteLine("\n=== 待执行任务 ===");
        foreach (var task in tasks)
        {
            if (!task.IsCompleted)
            {
                string type = task.IsRecurring ? "循环" : "单次";
                Console.WriteLine($"{task.Name} [{type}] - {task.ScheduledTime:HH:mm:ss}");
            }
        }
    }
}

class Program
{
    static void Main()
    {
        TaskScheduler scheduler = new TaskScheduler();
        
        // 订阅事件
        scheduler.TaskExecuted += task => 
            Console.WriteLine($"✅ 任务完成:{task.Name}");
        
        scheduler.TaskError += ex =>
            Console.WriteLine($"❌ 任务错误:{ex.Message}");
        
        // 添加各种任务(使用 Lambda)
        // 单次任务
        scheduler.AddTask(new ScheduledTask("发送早安邮件", 
            () => Console.WriteLine("  📧 早安邮件已发送"), 
            DateTime.Now.AddSeconds(3)));
        
        scheduler.AddTask(new ScheduledTask("备份数据库", 
            () => Console.WriteLine("  💾 数据库备份完成"), 
            DateTime.Now.AddSeconds(8)));
        
        // 循环任务
        scheduler.AddTask(new ScheduledTask("心跳检测", 
            () => Console.WriteLine("  💓 心跳检测... OK"), 
            TimeSpan.FromSeconds(5)));
        
        scheduler.AddTask(new ScheduledTask("打印当前时间", 
            () => Console.WriteLine($"  🕐 当前时间:{DateTime.Now:HH:mm:ss}"), 
            TimeSpan.FromSeconds(7)));
        
        // 可能会出错的任务
        scheduler.AddTask(new ScheduledTask("危险操作", 
            () => 
            {
                Console.WriteLine("  ⚠️ 执行危险操作...");
                throw new Exception("模拟异常");
            }, 
            DateTime.Now.AddSeconds(10)));
        
        // 显示任务列表
        scheduler.ShowPendingTasks();
        
        Console.WriteLine("\n等待任务执行...\n");
        
        // 启动调度器
        scheduler.Start();
        
        // 运行20秒
        Thread.Sleep(20000);
        
        // 停止调度器
        scheduler.Stop();
        
        // 显示最终状态
        scheduler.ShowPendingTasks();
    }
}

9.6 常见错误与陷阱

错误1:事件未检查 null

csharp

复制代码
public event Action MyEvent;

public void FireEvent()
{
    MyEvent();  // ❌ 如果没有订阅者,会抛出 NullReferenceException
}

// 正确
public void FireEvent()
{
    MyEvent?.Invoke();  // ✅
}

错误2:委托赋值 vs 添加

csharp

复制代码
Action d = Method1;
d += Method2;  // 添加方法
d = Method2;   // 替换(丢失了 Method1)

// 事件只能用 += 和 -=
event Action MyEvent;
MyEvent += Method1;  // ✅
MyEvent = Method1;   // ❌ 编译错误

错误3:Lambda 捕获变量陷阱

csharp

复制代码
// 问题:所有 lambda 捕获同一个变量
List<Action> actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
    actions.Add(() => Console.WriteLine(i));
}
foreach (var action in actions)
{
    action();  // 输出:5 5 5 5 5(不是 0 1 2 3 4)
}

// 解决方案:创建局部副本
for (int i = 0; i < 5; i++)
{
    int copy = i;
    actions.Add(() => Console.WriteLine(copy));
}

错误4:接口显式实现的调用

csharp

复制代码
interface IMyInterface
{
    void Method();
}

class MyClass : IMyInterface
{
    void IMyInterface.Method()  // 显式实现
    {
        Console.WriteLine("Hello");
    }
    
    public void Method()  // 普通实现
    {
        Console.WriteLine("World");
    }
}

MyClass obj = new MyClass();
obj.Method();           // World
// obj.Method2();       // 错误!不能直接调用显式实现的方法
((IMyInterface)obj).Method();  // Hello

9.7 本章总结

核心知识点导图

text

复制代码
面向对象编程(高级)
├── 接口
│   ├── 定义契约(能做什么)
│   ├── 多接口实现
│   ├── 接口继承
│   ├── 显式实现
│   └── 接口 vs 抽象类
│
├── 委托
│   ├── 方法的类型
│   ├── 多播委托
│   ├── 内置委托(Action, Func, Predicate)
│   └── 方法作为参数
│
├── 事件
│   ├── 基于委托的发布/订阅
│   ├── 标准 .NET 事件模式
│   ├── 事件 vs 委托
│   └── 自定义事件参数
│
└── Lambda 表达式
    ├── 匿名函数的简洁写法
    ├── 表达式 lambda
    ├── 语句 lambda
    └── 捕获变量

选择指南

场景 推荐使用
定义"能做什么"的能力 接口
提供默认实现 抽象类
方法作为参数传递 委托
通知变化/发布订阅 事件
简短的回调函数 Lambda
LINQ 查询 Lambda

9.8 练习题

基础题

  1. 定义接口 IVehicle,包含 Start()Stop() 方法。创建 CarBike 类实现该接口。

  2. 使用 Func 委托,编写一个方法,接收一个整数数组和一个 Func<int, bool> 谓词,返回满足条件的元素数组。

  3. 创建一个 Counter 类,当计数达到 10 的倍数时触发事件 OnMultipleReached

应用题

  1. 实现一个简单的插件系统:

    • 定义 IPlugin 接口(NameExecute()

    • 创建多个插件(LoggerPluginTimePluginMathPlugin

    • 创建 PluginManager 动态加载和执行插件

  2. 实现一个文件监视器:

    • 监视指定文件夹

    • 当文件创建、删除、修改时触发相应事件

    • 使用委托记录日志

挑战题

  1. 实现一个简单的 LINQ 风格的查询方法:

    • Where(条件过滤)

    • Select(投影转换)

    • OrderBy(排序)

    • 使用委托作为参数

  2. 实现一个异步任务管理器:

    • 支持添加任务(Action

    • 支持设置超时时间

    • 支持任务完成/失败/超时事件

    • 使用多线程执行

相关推荐
步步为营DotNet4 小时前
深入.NET 11:C# 14 在边缘计算数据处理的优化与实践
c#·.net·边缘计算
weixin_428005304 小时前
C#调用 AI学习从0开始-第1阶段(基础与工具)-第6天流式输出
开发语言·学习·c#·流式输出stream
xiaoshuaishuai84 小时前
C# Anthropic连接超时原因及方案
开发语言·网络·tcp/ip·c#
qingfeng154154 小时前
企业微信多账号协同管理方案:矩阵如何统一管理?
开发语言·python·自动化·企业微信
加号34 小时前
【C#】 实现 CRC16 校验:原理、算法与工程实践
算法·c#
小短腿的代码世界4 小时前
Qt属性系统揭秘:从Q_PROPERTY宏到动态元对象系统的完整架构解析
开发语言·qt·架构
HEADKON4 小时前
阿西米尼常见副作用血小板减少及高血压的临床特征与管理
c#
江屿风4 小时前
【C++笔记】内存管理流食般投喂
开发语言·c++·笔记