C# 学习笔记三:委托与事件、文件与 I/O 操作

老牛浏览 34评论 0发表于 更新于

目录

  1. 委托(Delegate)

  2. 事件(Event)

  3. 委托与事件的区别

  4. 文件与I/O操作

1. 委托(Delegate)

1.1 什么是委托

委托:类型安全的函数指针,可以像传递数据一样传递方法。

csharp
// 定义委托
public delegate int MathOperation(int a, int b);

class Program
{
    static int Add(int x, int y) => x + y;
    static int Subtract(int x, int y) => x - y;
    
    static void Main()
    {
        MathOperation operation = Add;
        int result = operation(10, 5);  // 15
        
        operation = Subtract;
        result = operation(10, 5);      // 5
    }
}

1.2 自定义委托

csharp
// 定义委托
public delegate bool FilterDelegate(int number);

class Program
{
    static bool IsEven(int n) => n % 2 == 0;
    static bool IsPositive(int n) => n > 0;
    
    static List<int> Filter(List<int> numbers, FilterDelegate filter)
    {
        List<int> result = new List<int>();
        foreach (int n in numbers)
        {
            if (filter(n)) result.Add(n);
        }
        return result;
    }
    
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
        List<int> evens = Filter(numbers, IsEven);
        List<int> positives = Filter(numbers, IsPositive);
    }
}

1.3 内置委托(Func、Action、Predicate)

C# 提供了泛型委托,无需自己定义:

委托类型

参数

返回值

示例

Action

0-16个

void

Action<string> print = Console.WriteLine;

Func

0-16个

最后一个类型参数

Func<int, int, int> add = (a,b) => a+b;

Predicate<T>

1个(T)

bool

Predicate<int> isEven = n => n%2==0;

csharp
class Program
{
    static void Main()
    {
        // Action:无返回值
        Action<string> print = Console.WriteLine;
        print("Hello");
        
        // Func:有返回值
        Func<int, int, int> add = (a, b) => a + b;
        int result = add(10, 20);  // 30
        
        // Predicate:返回 bool
        Predicate<int> isEven = n => n % 2 == 0;
        bool even = isEven(4);  // true
    }
}

1.4 Lambda 表达式(最常用)

csharp
// Lambda 语法:(参数) => 表达式或语句块

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

// 单个参数(可省略括号)
Func<int, int> square = x => x * x;

// 多个参数
Func<int, int, int> add = (a, b) => a + b;

// 语句块 Lambda
Func<int, int, int> calculate = (a, b) =>
{
    int sum = a + b;
    int product = a * b;
    return sum + product;
};

// 在 LINQ 中使用
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
List<int> evenNumbers = numbers.Where(n => n % 2 == 0).ToList();

1.5 多播委托(Multicast Delegate)

一个委托可以引用多个方法,调用时依次执行。

csharp
class Program
{
    static void Method1() => Console.WriteLine("方法1");
    static void Method2() => Console.WriteLine("方法2");
    static void Method3() => Console.WriteLine("方法3");
    
    static void Main()
    {
        Action multicast = Method1;
        multicast += Method2;   // 添加
        multicast += Method3;
        multicast();  // 输出:方法1 方法2 方法3
        
        multicast -= Method2;   // 移除
        multicast();  // 输出:方法1 方法3
    }
}

2. 事件(Event)

2.1 什么是事件

事件:基于委托的发布-订阅机制,让对象在特定情况下通知其他对象。

  • 发布者:触发事件的对象

  • 订阅者:响应事件的对象

  • 事件:发布者提供的"接口"

2.2 事件的标准模式

csharp
// 1. 事件参数类(继承 EventArgs)
public class TemperatureEventArgs : EventArgs
{
    public double Temperature { get; set; }
    public DateTime Time { get; set; }
    
    public TemperatureEventArgs(double temperature)
    {
        Temperature = temperature;
        Time = DateTime.Now;
    }
}

// 2. 发布者类
public class Thermometer
{
    private double _temperature;
    
    // 定义事件(使用 EventHandler<T>)
    public event EventHandler<TemperatureEventArgs> TemperatureChanged;
    
    // 触发事件的方法
    protected virtual void OnTemperatureChanged(TemperatureEventArgs e)
    {
        TemperatureChanged?.Invoke(this, e);
    }
    
    public void UpdateTemperature(double newTemp)
    {
        _temperature = newTemp;
        OnTemperatureChanged(new TemperatureEventArgs(_temperature));
    }
}

// 3. 订阅者类
public class Fan
{
    public void OnTemperatureChanged(object sender, TemperatureEventArgs e)
    {
        if (e.Temperature > 30)
            Console.WriteLine($"[风扇] 温度 {e.Temperature}°C,风扇打开!");
        else
            Console.WriteLine($"[风扇] 温度 {e.Temperature}°C,风扇关闭!");
    }
}

// 4. 使用
class Program
{
    static void Main()
    {
        Thermometer thermometer = new Thermometer();
        Fan fan = new Fan();
        
        // 订阅事件
        thermometer.TemperatureChanged += fan.OnTemperatureChanged;
        
        // 匿名方法订阅
        thermometer.TemperatureChanged += (sender, e) =>
        {
            Console.WriteLine($"[日志] 温度:{e.Temperature}°C");
        };
        
        // 触发事件
        thermometer.UpdateTemperature(32.5);
    }
}

2.3 完整事件示例(订单系统)

csharp
// 事件参数
public class OrderEventArgs : EventArgs
{
    public int OrderId { get; }
    public string CustomerName { get; }
    public decimal Amount { get; }
    
    public OrderEventArgs(int orderId, string customerName, decimal amount)
    {
        OrderId = orderId;
        CustomerName = customerName;
        Amount = amount;
    }
}

// 发布者
public class OrderService
{
    public event EventHandler<OrderEventArgs> OrderCreated;
    public event EventHandler<OrderEventArgs> OrderCompleted;
    
    protected virtual void OnOrderCreated(OrderEventArgs e)
        => OrderCreated?.Invoke(this, e);
    
    protected virtual void OnOrderCompleted(OrderEventArgs e)
        => OrderCompleted?.Invoke(this, e);
    
    public void CreateOrder(int orderId, string customer, decimal amount)
    {
        OnOrderCreated(new OrderEventArgs(orderId, customer, amount));
        // 处理订单...
        OnOrderCompleted(new OrderEventArgs(orderId, customer, amount));
    }
}

// 订阅者
public class EmailService
{
    public void OnOrderCreated(object sender, OrderEventArgs e)
        => Console.WriteLine($"[邮件] 订单 {e.OrderId} 已创建");
    
    public void OnOrderCompleted(object sender, OrderEventArgs e)
        => Console.WriteLine($"[邮件] 订单 {e.OrderId} 已完成");
}

// 使用
class Program
{
    static void Main()
    {
        OrderService service = new OrderService();
        EmailService email = new EmailService();
        
        service.OrderCreated += email.OnOrderCreated;
        service.OrderCompleted += email.OnOrderCompleted;
        
        service.CreateOrder(1001, "张三", 299.99m);
    }
}

3. 委托与事件的区别

特性

委托(Delegate)

事件(Event)

定义

public delegate ...

public event ...

用途

传递方法作为参数

发布-订阅通知

调用者

任何可以访问的地方

只能在定义类内部

外部调用

✅ 可以

❌ 不可以(编译错误)

多播

支持(+= / -=)

支持(+= / -=)

使用场景

回调、LINQ、异步

UI事件、消息通知

csharp
public class Example
{
    // 委托:外部可调用
    public Action<string> DelegateAction;
    
    // 事件:外部只能订阅/取消
    public event Action<string> EventAction;
    
    public void Test()
    {
        DelegateAction?.Invoke("Hello");  // ✅ 可以
        EventAction?.Invoke("Hello");     // ✅ 可以(类内部)
    }
}

class Program
{
    static void Main()
    {
        Example ex = new Example();
        
        ex.DelegateAction += Console.WriteLine;  // ✅ 订阅委托
        ex.EventAction += Console.WriteLine;     // ✅ 订阅事件
        
        ex.DelegateAction("委托调用");  // ✅ 可以调用委托
        // ex.EventAction("事件调用");  // ❌ 编译错误
    }
}

4. 文件与I/O操作

4.1 核心命名空间

csharp
using System.IO;           // 核心 I/O 类
using System.Text;         // 编码支持
using System.Threading.Tasks; // 异步操作

4.2 核心类概览

类名

用途

特点

File

文件操作(静态方法)

简单快速,适合小文件

FileInfo

文件信息(实例方法)

更灵活,适合多次操作

Directory

目录操作(静态方法)

创建、删除、遍历目录

DirectoryInfo

目录信息(实例方法)

详细目录信息

Path

路径处理

跨平台路径操作

FileStream

文件流

底层读写,适合大文件

StreamReader/Writer

文本读写

处理文本文件

BinaryReader/Writer

二进制读写

处理二进制数据

4.3 文本文件操作

csharp
// 快速读写(适合小文件)
string path = "example.txt";

// 写入文本
File.WriteAllText(path, "Hello, C#!");

// 读取所有文本
string content = File.ReadAllText(path);

// 按行写入
string[] lines = { "行1", "行2", "行3" };
File.WriteAllLines("lines.txt", lines);

// 按行读取
string[] readLines = File.ReadAllLines("lines.txt");

// 追加文本
File.AppendAllText(path, "\n追加的内容");

// 使用 StreamReader/Writer(适合大文件)
using (StreamWriter writer = new StreamWriter(path))
{
    for (int i = 0; i < 10; i++)
    {
        writer.WriteLine($"第 {i + 1} 行");
    }
}

using (StreamReader reader = new StreamReader(path))
{
    string line;
    while ((line = reader.ReadLine()) != null)
    {
        Console.WriteLine(line);
    }
}

// 指定编码
using (StreamReader reader = new StreamReader(path, Encoding.UTF8))
{
    string content = reader.ReadToEnd();
}

4.4 using 语句块 vs using 声明

特性

using 语句块

using 声明

语法

using (var x = new X()) { }

using var x = new X();

作用域

大括号 { }

当前代码块

释放时机

离开大括号时

离开当前代码块时

C#版本

1.0+

8.0+

csharp
// using 语句块(传统)
using (StreamWriter writer = new StreamWriter(path))
{
    writer.WriteLine("Hello");
}  // 离开块时释放

// using 声明(C# 8.0+)
using StreamWriter writer = new StreamWriter(path);
writer.WriteLine("Hello");
// 当前方法结束时释放

4.5 路径操作(Path 类)

csharp
string path = @"C:\Projects\MyApp\data\settings.json";

Console.WriteLine($"目录:{Path.GetDirectoryName(path)}");
Console.WriteLine($"文件名:{Path.GetFileName(path)}");
Console.WriteLine($"扩展名:{Path.GetExtension(path)}");
Console.WriteLine($"文件名(无扩展名):{Path.GetFileNameWithoutExtension(path)}");

// 组合路径
string combined = Path.Combine(@"C:\Projects", "data", "settings.json");

// 获取临时文件
string tempFile = Path.GetTempFileName();

// 获取应用程序目录
string appDir = AppDomain.CurrentDomain.BaseDirectory;

4.6 目录操作

csharp
string path = @"C:\Temp\MyApp";

// 检查目录是否存在
if (!Directory.Exists(path))
{
    Directory.CreateDirectory(path);
}

// 获取子目录
string[] subDirs = Directory.GetDirectories(path);

// 获取所有文件(递归)
string[] files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories);

// 删除目录
Directory.Delete(path, true);

4.7 文件信息操作(FileInfo)

csharp
FileInfo fileInfo = new FileInfo("example.txt");

Console.WriteLine($"文件名:{fileInfo.Name}");
Console.WriteLine($"大小:{fileInfo.Length} bytes");
Console.WriteLine($"创建时间:{fileInfo.CreationTime}");
Console.WriteLine($"最后修改:{fileInfo.LastWriteTime}");
Console.WriteLine($"只读:{fileInfo.IsReadOnly}");

// 复制文件
fileInfo.CopyTo("example_copy.txt");

// 删除文件
fileInfo.Delete();

4.8 二进制文件操作

csharp
string path = "data.bin";

// 写入二进制数据
using (BinaryWriter writer = new BinaryWriter(File.Open(path, FileMode.Create)))
{
    writer.Write(10);                    // int
    writer.Write(3.14);                  // double
    writer.Write(true);                  // bool
    writer.Write("Hello");               // string
    writer.Write(new byte[] { 1, 2, 3 }); // byte[]
}

// 读取二进制数据(按相同顺序)
using (BinaryReader reader = new BinaryReader(File.Open(path, FileMode.Open)))
{
    int intValue = reader.ReadInt32();
    double doubleValue = reader.ReadDouble();
    bool boolValue = reader.ReadBoolean();
    string stringValue = reader.ReadString();
    byte[] bytes = reader.ReadBytes(3);
}

4.9 异步文件操作

csharp
async Task WriteFileAsync(string path, string content)
{
    byte[] data = Encoding.UTF8.GetBytes(content);
    using (FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write))
    {
        await fs.WriteAsync(data, 0, data.Length);
    }
}

async Task<string> ReadFileAsync(string path)
{
    using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
    {
        byte[] buffer = new byte[fs.Length];
        await fs.ReadAsync(buffer, 0, buffer.Length);
        return Encoding.UTF8.GetString(buffer);
    }
}

async Task CopyFileAsync(string source, string destination)
{
    using (FileStream sourceStream = File.OpenRead(source))
    using (FileStream destStream = File.Create(destination))
    {
        await sourceStream.CopyToAsync(destStream);
    }
}

4.10 文件操作最佳实践

csharp
// ✅ 使用 using 确保资源释放
using (var writer = new StreamWriter("file.txt"))
{
    writer.WriteLine("Hello");
}

// ✅ 检查文件是否存在
if (File.Exists(path))
{
    string content = File.ReadAllText(path);
}

// ✅ 使用 try-catch 处理异常
try
{
    File.WriteAllText(path, content);
}
catch (IOException ex)
{
    Console.WriteLine($"文件操作失败:{ex.Message}");
}
catch (UnauthorizedAccessException ex)
{
    Console.WriteLine($"权限不足:{ex.Message}");
}

// ✅ 使用 Path.Combine 组合路径
string fullPath = Path.Combine(appDirectory, "data", "file.txt");

// ❌ 避免:直接拼接路径
// string path = appDir + "\\" + "data" + "\\" + "file.txt";

// ❌ 避免:不释放资源
// StreamReader reader = new StreamReader(path);
// string content = reader.ReadToEnd();  // 忘记关闭

📝 完整示例:文件配置管理器

csharp
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;

public class AppConfig
{
    public string AppName { get; set; } = "MyApp";
    public string Version { get; set; } = "1.0.0";
    public int MaxRetries { get; set; } = 3;
    public bool DebugMode { get; set; } = true;
    public Dictionary<string, string> ConnectionStrings { get; set; } = new();
}

public class ConfigManager
{
    private readonly string _configPath;
    private AppConfig _config;
    
    public ConfigManager(string configPath = "appsettings.json")
    {
        _configPath = configPath;
        LoadOrCreate();
    }
    
    public AppConfig Config => _config;
    
    private void LoadOrCreate()
    {
        if (File.Exists(_configPath))
            Load();
        else
            CreateDefault();
    }
    
    private void Load()
    {
        try
        {
            string json = File.ReadAllText(_configPath);
            _config = JsonSerializer.Deserialize<AppConfig>(json);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"加载配置失败:{ex.Message}");
            CreateDefault();
        }
    }
    
    private void CreateDefault()
    {
        _config = new AppConfig();
        Save();
    }
    
    public void Save()
    {
        try
        {
            string json = JsonSerializer.Serialize(_config, new JsonSerializerOptions
            {
                WriteIndented = true
            });
            File.WriteAllText(_configPath, json);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"保存配置失败:{ex.Message}");
        }
    }
}

📊 知识点速查表

委托速查

类型

定义

用途

自定义委托

delegate 返回值 名称(参数)

自定义回调

Action

无返回值

执行操作

Func

有返回值

计算并返回

Predicate<T>

返回 bool

条件判断

Lambda

(参数) => 表达式

简洁匿名方法

事件速查

步骤

代码

定义事件参数

class MyEventArgs : EventArgs

定义事件

public event EventHandler<MyEventArgs> MyEvent;

触发事件

MyEvent?.Invoke(this, e);

订阅事件

obj.MyEvent += handler;

取消订阅

obj.MyEvent -= handler;

文件操作速查

操作

方法

读写全部文本

File.ReadAllText / File.WriteAllText

读写全部行

File.ReadAllLines / File.WriteAllLines

逐行读取

StreamReader.ReadLine

逐行写入

StreamWriter.WriteLine

二进制读写

BinaryReader / BinaryWriter

异步读写

ReadAsync / WriteAsync

路径操作

Path

目录操作

Directory

点赞
收藏
暂无评论,快来发表评论吧~