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 练习题
基础题
-
定义接口
IVehicle,包含Start()、Stop()方法。创建Car和Bike类实现该接口。 -
使用
Func委托,编写一个方法,接收一个整数数组和一个Func<int, bool>谓词,返回满足条件的元素数组。 -
创建一个
Counter类,当计数达到 10 的倍数时触发事件OnMultipleReached。
应用题
-
实现一个简单的插件系统:
-
定义
IPlugin接口(Name、Execute()) -
创建多个插件(
LoggerPlugin、TimePlugin、MathPlugin) -
创建
PluginManager动态加载和执行插件
-
-
实现一个文件监视器:
-
监视指定文件夹
-
当文件创建、删除、修改时触发相应事件
-
使用委托记录日志
-
挑战题
-
实现一个简单的 LINQ 风格的查询方法:
-
Where(条件过滤) -
Select(投影转换) -
OrderBy(排序) -
使用委托作为参数
-
-
实现一个异步任务管理器:
-
支持添加任务(
Action) -
支持设置超时时间
-
支持任务完成/失败/超时事件
-
使用多线程执行
-