文件与 IO
在 AutoCAD/Revit 插件开发中,读写配置文件、导出数据、日志记录是高频需求。C# 提供了层次清晰的 IO API。
System.IO 核心类一览
| 类 | 用途 | 静态方法 | 实例用法 |
|---|---|---|---|
File |
文件 CRUD(静态) | ReadAllText, WriteAllText, Exists, Delete, Copy |
一次性读写 |
FileInfo |
文件 CRUD(实例) | — | 需要多次操作同一文件时 |
Directory |
目录 CRUD(静态) | CreateDirectory, GetFiles, Exists |
一次性操作 |
DirectoryInfo |
目录 CRUD(实例) | — | 枚举文件、递归遍历 |
Path |
路径操作(静态) | Combine, GetFileName, GetExtension, GetTempPath |
路径拼接与拆解 |
Stream |
抽象基类 | — | 所有流 IO 的基类 |
路径操作
using System.IO;
// ✅ 始终使用 Path.Combine,不要手动拼接字符串
string folder = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string filePath = Path.Combine(folder, "MyAddin", "config.json");
// 路径信息提取
string? dir = Path.GetDirectoryName(filePath); // C:\Users\...\MyAddin
string? ext = Path.GetExtension(filePath); // .json
string? name = Path.GetFileNameWithoutExtension(filePath); // config
string? fullName = Path.GetFileName(filePath); // config.json
// 临时文件
string tmp = Path.GetTempFileName();
string tmpDir = Path.GetTempPath();
// 获取当前 DLL 所在目录(AutoCAD/Revit 插件中的常用模式)
string addinPath = typeof(MyCommand).Assembly.Location;
string addinDir = Path.GetDirectoryName(addinPath);🚨 Windows 路径中反斜杠
\在 C# 字符串中需要转义\\,或用@"..."逐字字符串:@"C:\Users\Willow"。
文件读写
文本文件
// 一次性读取/写入
string content = File.ReadAllText(filePath);
string[] lines = File.ReadAllLines(filePath);
File.WriteAllText(filePath, "Hello World");
File.WriteAllLines(filePath, new[] { "line1", "line2" });
File.AppendAllText(filePath, "追加内容" + Environment.NewLine);
// 检测文件是否存在
if (File.Exists(filePath))
{
// ...
}Stream 流式读写
// StreamReader —— 大文件逐行读取
using var reader = new StreamReader(filePath);
while (!reader.EndOfStream)
{
string? line = reader.ReadLine();
Console.WriteLine(line);
}
// StreamWriter —— 逐行写入
using var writer = new StreamWriter(filePath, append: false);
writer.WriteLine("第一行");
writer.WriteLine("第二行");
writer.Flush(); // 强制刷入磁盘二进制文件
// 一次性
byte[] data = File.ReadAllBytes(filePath);
File.WriteAllBytes(filePath, data);
// 流式(大文件)
using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
// 处理 buffer[0..bytesRead]
}文件与目录操作
// 目录操作
Directory.CreateDirectory(@"C:\MyApp\Data");
bool dirExists = Directory.Exists(@"C:\MyApp\Data");
string[] files = Directory.GetFiles(@"C:\MyApp", "*.log", SearchOption.AllDirectories);
// 文件操作
File.Copy(source, dest, overwrite: true);
File.Move(source, dest);
File.Delete(filePath);
// FileInfo(实例,适合多次操作同一文件)
var fileInfo = new FileInfo(filePath);
long size = fileInfo.Length;
DateTime lastModified = fileInfo.LastWriteTime;
fileInfo.MoveTo(newPath);
// 文件属性
File.SetAttributes(filePath, FileAttributes.Hidden);
FileAttributes attr = File.GetAttributes(filePath);序列化
JSON(System.Text.Json)
using System.Text.Json;
public class AddinConfig
{
public string LicenseKey { get; set; } = "";
public int MaxRetry { get; set; } = 3;
public List<string> EnabledCommands { get; set; } = new();
}
// 序列化
var config = new AddinConfig { LicenseKey = "ABC-123", MaxRetry = 5 };
string json = JsonSerializer.Serialize(config, new JsonSerializerOptions
{
WriteIndented = true // 格式化输出
});
// 反序列化
AddinConfig? loaded = JsonSerializer.Deserialize<AddinConfig>(json);
// 文件快捷方法
await JsonSerializer.SerializeAsync(File.Create(filePath), config);
AddinConfig? cfg = await JsonSerializer.DeserializeAsync<AddinConfig>(File.OpenRead(filePath));🚨 .NET Framework 4.8 不内置 System.Text.Json,需要用 NuGet 安装
System.Text.Json包,或使用Newtonsoft.Json。
XML
using System.Xml.Serialization;
[XmlRoot("Config")]
public class AddinConfig
{
[XmlElement("LicenseKey")]
public string LicenseKey { get; set; } = "";
[XmlAttribute("Version")]
public string Version { get; set; } = "1.0";
}
// 序列化
var serializer = new XmlSerializer(typeof(AddinConfig));
using var writer = new StreamWriter(filePath);
serializer.Serialize(writer, config);
// 反序列化
using var reader = new StreamReader(filePath);
AddinConfig? loaded = (AddinConfig)serializer.Deserialize(reader);using 语句与资源释放
// using 声明(C# 8+)—— 离开作用域自动 Dispose
using var stream = new FileStream(filePath, FileMode.Open);
// using 语句块 —— 明确作用域
using (var fs = new FileStream(filePath, FileMode.Open))
using (var reader = new StreamReader(fs))
{
return reader.ReadToEnd();
}🚨 任何实现了
IDisposable的对象都必须using或手动Dispose()。Stream、StreamReader/Writer、SqlConnection、Transaction等都需要释放。
AutoCAD / Revit 中的 IO 场景
常见需求
| 场景 | 推荐方案 |
|---|---|
| 插件配置文件 | JSON + File.ReadAllText |
| 导出 CSV/Excel | StreamWriter 逐行写入 |
| 日志记录 | File.AppendAllText 或 Serilog/NLog |
| 读取项目资源 | Assembly.GetManifestResourceStream |
| 用户数据目录 | Environment.SpecialFolder.ApplicationData + 插件名子目录 |
典型模式:插件配置文件
public static class AddinSettings
{
private static readonly string _path = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"MyAddin",
"settings.json"
);
public static Config Load()
{
if (!File.Exists(_path)) return new Config();
string json = File.ReadAllText(_path);
return JsonSerializer.Deserialize<Config>(json) ?? new Config();
}
public static void Save(Config config)
{
Directory.CreateDirectory(Path.GetDirectoryName(_path)!);
string json = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(_path, json);
}
}