Initial commit: C# 学习笔记和示例代码

- Lesson 01-10: C# 基础语法
- WebView2: 集成示例
- notes/: 详细笔记
This commit is contained in:
Rosmontis_Cloud 2026-07-01 16:31:35 +08:00
commit f604d53191
51 changed files with 2745 additions and 0 deletions

25
.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
# .NET
bin/
obj/
*.user
*.suo
*.cache
*.log
# IDE
.vs/
.vscode/
*.swp
# Build results
[Dd]ebug/
[Rr]elease/
x64/
x86/
# NuGet
packages/
*.nupkg
# WebView2
*.WebView2/

1
WebView2Demo Submodule

@ -0,0 +1 @@
Subproject commit 1d85cb160f81d5a41125d348f97c01b2db0a09b4

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,67 @@
using System;
namespace Lesson02
{
class Program
{
static void Main(string[] args)
{
// ========== 变量 ==========
int age = 25;
double height = 175.5;
string name = "无言势字幕";
char grade = 'A';
bool isActive = true;
Console.WriteLine("=== 变量 ===");
Console.WriteLine($"姓名: {name}");
Console.WriteLine($"年龄: {age}");
Console.WriteLine($"身高: {height}cm");
Console.WriteLine($"等级: {grade}");
Console.WriteLine($"状态: {isActive}");
// ========== var 自动推断 ==========
Console.WriteLine("\n=== var 自动推断 ===");
var message = "这是 string 类型";
var number = 42; // 推断为 int
Console.WriteLine($"message 是: {message.GetType()}");
Console.WriteLine($"number 是: {number.GetType()}");
// ========== 常量 ==========
Console.WriteLine("\n=== 常量 ===");
const int MAX_RETRY = 3;
const string VERSION = "v1.0";
Console.WriteLine($"最大重试次数: {MAX_RETRY}");
Console.WriteLine($"版本: {VERSION}");
// ========== 类型转换 ==========
Console.WriteLine("\n=== 类型转换 ===");
int i = 10;
double d = i; // 隐式转换
Console.WriteLine($"int -> double: {d}");
double d2 = 3.14;
int i2 = (int)d2; // 显式转换
Console.WriteLine($"double -> int: {i2}");
string s = i.ToString();
Console.WriteLine($"int -> string: {s}");
int i3 = int.Parse("123");
Console.WriteLine($"string \"123\" -> int: {i3}");
// ========== 输入练习 ==========
Console.WriteLine("\n=== 输入练习 ===");
Console.Write("请输入你的名字: ");
string inputName = Console.ReadLine();
Console.Write($"你好, {inputName}! ");
Console.Write("请输入年龄: ");
string ageInput = Console.ReadLine();
int userAge = int.Parse(ageInput);
Console.WriteLine($"你 {userAge} 岁了");
Console.ReadLine();
}
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,96 @@
using System;
namespace Lesson03
{
class Program
{
static void Main(string[] args)
{
// ========== if / else ==========
Console.WriteLine("=== if / else ===");
int score = 85;
if (score >= 90)
{
Console.WriteLine("优秀");
}
else if (score >= 60)
{
Console.WriteLine("及格");
}
else
{
Console.WriteLine("不及格");
}
// 三元运算符
string result = score >= 60 ? "及格" : "不及格";
Console.WriteLine($"三元运算: {result}");
// ========== switch ==========
Console.WriteLine("\n=== switch ===");
int day = 3;
switch (day)
{
case 1:
Console.WriteLine("星期一");
break;
case 2:
Console.WriteLine("星期二");
break;
case 3:
Console.WriteLine("星期三");
break;
default:
Console.WriteLine("其他");
break;
}
// ========== for 循环 ==========
Console.WriteLine("\n=== for 循环 ===");
for (int i = 0; i < 5; i++)
{
Console.WriteLine($"计数: {i}");
}
// ========== foreach ==========
Console.WriteLine("\n=== foreach 循环 ===");
string[] names = { "无言势", "VRChat", "字幕" };
foreach (string name in names)
{
Console.WriteLine(name);
}
// ========== while 循环 ==========
Console.WriteLine("\n=== while 循环 ===");
int count = 0;
while (count < 3)
{
Console.WriteLine($"while: {count}");
count++;
}
// ========== do while ==========
Console.WriteLine("\n=== do while ===");
int n = 0;
do
{
Console.WriteLine($"do while: {n}");
n++;
} while (n < 3);
// ========== break 和 continue ==========
Console.WriteLine("\n=== break 和 continue ===");
for (int i = 0; i < 10; i++)
{
if (i == 3) continue; // 跳过 3
if (i == 7) break; // 退出循环
Console.WriteLine(i);
}
Console.ReadLine();
}
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

119
lessons/Lesson04/Program.cs Normal file
View File

@ -0,0 +1,119 @@
using System;
namespace Lesson04
{
class Program
{
static void Main(string[] args)
{
// ========== 方法调用 ==========
Console.WriteLine("=== 方法 ===");
SayHello("无言势");
int sum = Add(10, 20);
Console.WriteLine($"10 + 20 = {sum}");
// 带默认参数
Greet();
Greet("VRC");
// ========== 类和对象 ==========
Console.WriteLine("\n=== 类和对象 ===");
Person p1 = new Person("无言势", 25);
Person p2 = new Person("玩家A", 30);
p1.SayHi();
p2.SayHi();
// ========== 静态方法 ==========
Console.WriteLine("\n=== 静态方法 ===");
int result = MathHelper.Add(100, 200);
Console.WriteLine($"100 + 200 = {result}");
double pi = MathHelper.GetPI();
Console.WriteLine($"PI ≈ {pi}");
// ========== 面向对象练习 ==========
Console.WriteLine("\n=== 麦克风类 ===");
Microphone mic = new Microphone("USB麦克风", true);
mic.ToggleListening();
mic.ToggleListening();
Console.ReadLine();
}
// ========== 方法定义 ==========
static void SayHello(string name)
{
Console.WriteLine($"你好, {name}!");
}
static int Add(int a, int b)
{
return a + b;
}
static void Greet(string name = "World")
{
Console.WriteLine($"Hello, {name}");
}
}
// ========== 类定义 ==========
class Person
{
public string name;
public int age;
// 构造函数
public Person(string name, int age)
{
this.name = name;
this.age = age;
}
public void SayHi()
{
Console.WriteLine($"我是{name},今年{age}岁");
}
}
// 静态类
static class MathHelper
{
public static int Add(int a, int b)
{
return a + b;
}
public static double GetPI()
{
return 3.14159;
}
}
// 麦克风类 - 结合项目练习
class Microphone
{
private string name;
private bool isListening;
public Microphone(string name, bool isListening)
{
this.name = name;
this.isListening = isListening;
}
public void ToggleListening()
{
isListening = !isListening;
if (isListening)
{
Console.WriteLine($"[{name}] 开始监听...");
}
else
{
Console.WriteLine($"[{name}] 停止监听");
}
}
}
}

View File

@ -0,0 +1,27 @@
using System;
namespace Lesson05.Helpers
{
static class MathHelper
{
public static int Add(int a, int b)
{
return a + b;
}
public static int Multiply(int a, int b)
{
return a * b;
}
public static double Divide(int a, int b)
{
if (b == 0)
{
Console.WriteLine("除数不能为0");
return 0;
}
return (double)a / b;
}
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,46 @@
using System;
namespace Lesson05.Models
{
class Person
{
// public: 任何地方都能访问
public string name;
// private: 只有本类内部能访问
private int age;
private string secret = "秘密信息";
// Property: 受保护的访问方式
public int Age
{
get { return age; }
set
{
// value 是关键字,表示赋值时的右值
if (value < 0) value = 0;
if (value > 150) value = 150;
age = value;
}
}
// 构造函数
public Person(string name, int age)
{
this.name = name;
this.Age = age; // 通过 Property 设置,自动限制范围
}
public void SayHi()
{
Console.WriteLine($"我是 {name},今年 {age} 岁");
Console.WriteLine($"我的秘密: {GetSecret()}"); // 本类内可以调用私有方法
}
// 私有方法:本类内部使用
private string GetSecret()
{
return secret;
}
}
}

View File

@ -0,0 +1,42 @@
using System;
using Lesson05.Models;
using Lesson05.Services;
using Lesson05.Helpers;
namespace Lesson05
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=== 多文件项目演示 ===\n");
// 使用 Models 里的类
Person p = new Person("无言势", 25);
p.SayHi();
// 使用 Property
p.Age = -5; // 会自动限制范围
Console.WriteLine($"设置后年龄: {p.Age}");
p.Age = 200;
Console.WriteLine($"设置后年龄: {p.Age}");
// 使用 Services
AudioService service = new AudioService();
service.StartListening();
service.StopListening();
// 使用 Helpers
int sum = MathHelper.Add(100, 200);
Console.WriteLine($"\n100 + 200 = {sum}");
// 演示 private 无法访问
// p.secret = "xxx"; // 编译错误
// p.SetSecret("xxx"); // 编译错误
Console.WriteLine("\n=== 演示完成 ===");
Console.ReadLine();
}
}
}

View File

@ -0,0 +1,39 @@
using System;
namespace Lesson05.Services
{
class AudioService
{
private bool isListening;
private string deviceName;
public AudioService()
{
this.deviceName = "默认麦克风";
this.isListening = false;
}
public void StartListening()
{
if (!isListening)
{
isListening = true;
Console.WriteLine($"[{deviceName}] 开始监听...");
}
}
public void StopListening()
{
if (isListening)
{
isListening = false;
Console.WriteLine($"[{deviceName}] 停止监听");
}
}
public bool IsListening()
{
return isListening;
}
}
}

View File

@ -0,0 +1,25 @@
using System;
namespace Lesson06.Animals
{
class Animal
{
protected string name;
public Animal(string name)
{
this.name = name;
}
public void Eat()
{
Console.WriteLine($"{name} 在吃东西");
}
// virtual 允许子类重写
public virtual void Speak()
{
Console.WriteLine($"{name} 发出了声音");
}
}
}

View File

@ -0,0 +1,16 @@
using System;
namespace Lesson06.Animals
{
class Cat : Animal
{
public Cat(string name) : base(name)
{
}
public override void Speak()
{
Console.WriteLine($"{name} 喵喵喵~");
}
}
}

View File

@ -0,0 +1,27 @@
using System;
namespace Lesson06.Animals
{
class Dog : Animal
{
private string breed;
// base(name) 调用父类的构造函数
public Dog(string name, string breed) : base(name)
{
this.breed = breed;
}
public void Info()
{
Console.WriteLine($"名字: {name}, 品种: {breed}");
}
// 重写父类方法
public override void Speak()
{
base.Speak(); // 先调用父类方法
Console.WriteLine($"{name} 汪汪汪!");
}
}
}

View File

@ -0,0 +1,34 @@
using System;
namespace Lesson06.Devices
{
class Camera : IDevice
{
private string name;
public bool IsConnected { get; private set; }
public Camera(string name)
{
this.name = name;
this.IsConnected = false;
}
public void Connect()
{
if (!IsConnected)
{
IsConnected = true;
Console.WriteLine($"[{name}] 摄像头已连接");
}
}
public void Disconnect()
{
if (IsConnected)
{
IsConnected = false;
Console.WriteLine($"[{name}] 摄像头已断开");
}
}
}
}

View File

@ -0,0 +1,10 @@
namespace Lesson06.Devices
{
// 接口:定义契约
interface IDevice
{
void Connect();
void Disconnect();
bool IsConnected { get; }
}
}

View File

@ -0,0 +1,34 @@
using System;
namespace Lesson06.Devices
{
class Microphone : IDevice
{
private string name;
public bool IsConnected { get; private set; }
public Microphone(string name)
{
this.name = name;
this.IsConnected = false;
}
public void Connect()
{
if (!IsConnected)
{
IsConnected = true;
Console.WriteLine($"[{name}] 麦克风已连接");
}
}
public void Disconnect()
{
if (IsConnected)
{
IsConnected = false;
Console.WriteLine($"[{name}] 麦克风已断开");
}
}
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,55 @@
using System;
using Lesson06.Animals;
using Lesson06.Devices;
namespace Lesson06
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=== 继承演示 ===\n");
// 继承Dog 继承了 Animal
Dog dog = new Dog("旺财", "金毛");
dog.Eat();
dog.Speak();
dog.Info();
Console.WriteLine("\n=== 方法重写 ===\n");
// 多态Cat 和 Dog 重写了 Speak
Animal cat = new Cat("小猫");
Animal dog2 = new Dog("二狗", "哈士奇");
cat.Speak();
dog2.Speak();
Console.WriteLine("\n=== 接口演示 ===\n");
// 使用接口
Microphone mic = new Microphone("USB麦克风");
Camera cam = new Camera("RTSP摄像头");
mic.Connect();
mic.Disconnect();
Console.WriteLine();
cam.Connect();
cam.Disconnect();
Console.WriteLine("\n=== 接口多态 ===\n");
// 接口类型可以指向任何实现它的类
IDevice device1 = new Microphone("设备A");
IDevice device2 = new Camera("设备B");
device1.Connect();
device2.Connect();
Console.WriteLine("\n=== 完成 ===");
Console.ReadLine();
}
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

107
lessons/Lesson07/Program.cs Normal file
View File

@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
namespace Lesson07
{
class Program
{
static void Main(string[] args)
{
// ========== List<T> ==========
Console.WriteLine("=== List<T> 动态数组 ===\n");
List<string> transcriptHistory = new List<string>();
// 添加识别结果
transcriptHistory.Add("今天天气真不错");
transcriptHistory.Add("大家一起加油");
transcriptHistory.Add("这个功能很简单");
Console.WriteLine($"共有 {transcriptHistory.Count} 条记录:");
foreach (string text in transcriptHistory)
{
Console.WriteLine($" - {text}");
}
// 插入
transcriptHistory.Insert(1, "新插入的文字");
Console.WriteLine($"\n插入后共有 {transcriptHistory.Count} 条记录:");
foreach (string text in transcriptHistory)
{
Console.WriteLine($" - {text}");
}
// 删除
transcriptHistory.Remove("大家一起加油");
Console.WriteLine($"\n删除后共有 {transcriptHistory.Count} 条记录");
// 初始化器
List<string> fruits = new List<string>() { "苹果", "香蕉", "橙子" };
Console.WriteLine($"\n水果列表{string.Join(", ", fruits)}");
// ========== Dictionary<K, V> ==========
Console.WriteLine("\n=== Dictionary<K, V> 键值对 ===\n");
// 设备配置
Dictionary<string, string> deviceConfig = new Dictionary<string, string>()
{
["name"] = "USB麦克风",
["sampleRate"] = "16000",
["channels"] = "1"
};
Console.WriteLine("设备配置:");
foreach (KeyValuePair<string, string> kv in deviceConfig)
{
Console.WriteLine($" {kv.Key}: {kv.Value}");
}
// 访问单个值
Console.WriteLine($"\n设备名称: {deviceConfig["name"]}");
// 安全访问
if (deviceConfig.TryGetValue("notExist", out string value))
{
Console.WriteLine($"找到: {value}");
}
else
{
Console.WriteLine("键不存在");
}
// 设置选项
Dictionary<string, bool> settings = new Dictionary<string, bool>()
{
["autoConnect"] = true,
["vad"] = true,
["debug"] = false
};
Console.WriteLine("\n设置选项");
foreach (string key in settings.Keys)
{
string status = settings[key] ? "开启" : "关闭";
Console.WriteLine($" {key}: {status}");
}
// ========== 数字集合 ==========
Console.WriteLine("\n=== 数字 List 操作 ===\n");
List<int> scores = new List<int>() { 85, 92, 78, 95, 88 };
Console.WriteLine($"原始成绩:{string.Join(", ", scores)}");
scores.Sort();
Console.WriteLine($"排序后:{string.Join(", ", scores)}");
scores.Reverse();
Console.WriteLine($"反转后:{string.Join(", ", scores)}");
Console.WriteLine($"最高分:{scores[0]}");
Console.WriteLine($"包含 100 {scores.Contains(100)}");
Console.WriteLine($"包含 95 {scores.Contains(95)}");
Console.ReadLine();
}
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

187
lessons/Lesson08/Program.cs Normal file
View File

@ -0,0 +1,187 @@
using System;
namespace Lesson08
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=== 异常处理演示 ===\n");
// ========== 基本 try-catch ==========
Console.WriteLine("--- 基本 try-catch ---");
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[10]);
}
catch (Exception ex)
{
Console.WriteLine($"捕获异常: {ex.Message}");
}
// ========== 捕获特定异常 ==========
Console.WriteLine("\n--- 捕获特定异常 ---");
string[] inputs = { "123", "abc", "9999999999999" };
foreach (string input in inputs)
{
try
{
int num = int.Parse(input);
Console.WriteLine($"转换成功: {num}");
}
catch (FormatException)
{
Console.WriteLine($"\"{input}\" 格式错误,不是数字");
}
catch (OverflowException)
{
Console.WriteLine($"\"{input}\" 数字太大");
}
}
// ========== throw 抛出异常 ==========
Console.WriteLine("\n--- throw 抛出异常 ---");
try
{
SetAge(-5);
}
catch (ArgumentException ex)
{
Console.WriteLine($"捕获到: {ex.Message}");
}
try
{
SetAge(200);
}
catch (ArgumentException ex)
{
Console.WriteLine($"捕获到: {ex.Message}");
}
SetAge(25); // 正常
SetAge(0); // 正常
// ========== 实际项目场景 ==========
Console.WriteLine("\n--- 设备连接场景 ---");
MicrophoneDevice mic = new MicrophoneDevice("USB麦克风");
// 场景1设备未连接
try
{
mic.StartListening();
}
catch (DeviceNotConnectedException ex)
{
Console.WriteLine($"错误: {ex.Message}");
Console.WriteLine("建议:请检查设备连接");
}
// 场景2连接后正常操作
mic.Connect();
try
{
mic.StartListening();
Console.WriteLine("麦克风开始监听...");
mic.StopListening();
Console.WriteLine("麦克风停止监听...");
}
catch (DeviceNotConnectedException ex)
{
Console.WriteLine($"错误: {ex.Message}");
}
finally
{
mic.Disconnect();
Console.WriteLine("设备已断开");
}
// ========== finally 演示 ==========
Console.WriteLine("\n--- finally 执行时机 ---");
try
{
Console.WriteLine("try 块执行");
// int x = 1 / 0; // 解开这行会跳到 catch
}
catch
{
Console.WriteLine("catch 块执行");
}
finally
{
Console.WriteLine("finally 块总是执行");
}
Console.WriteLine("\n=== 演示完成 ===");
Console.ReadLine();
}
// ========== throw 示例 ==========
static void SetAge(int age)
{
if (age < 0 || age > 150)
{
throw new ArgumentException($"年龄必须在 0-150 之间,当前: {age}");
}
Console.WriteLine($"年龄设置为: {age}");
}
}
// ========== 自定义异常 ==========
class DeviceNotConnectedException : Exception
{
public DeviceNotConnectedException(string message) : base(message) { }
public DeviceNotConnectedException(string message, Exception inner) : base(message, inner) { }
}
// ========== 设备类 ==========
class MicrophoneDevice
{
private string name;
private bool isConnected;
private bool isListening;
public MicrophoneDevice(string name)
{
this.name = name;
this.isConnected = false;
this.isListening = false;
}
public void Connect()
{
isConnected = true;
Console.WriteLine($"[{name}] 设备已连接");
}
public void Disconnect()
{
isConnected = false;
Console.WriteLine($"[{name}] 设备已断开");
}
public void StartListening()
{
if (!isConnected)
{
throw new DeviceNotConnectedException($"{name} 未连接,无法开始监听");
}
if (isListening)
{
throw new InvalidOperationException($"{name} 已经在监听中");
}
isListening = true;
}
public void StopListening()
{
if (!isListening)
{
throw new InvalidOperationException($"{name} 没有在监听");
}
isListening = false;
}
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

103
lessons/Lesson09/Program.cs Normal file
View File

@ -0,0 +1,103 @@
using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Lesson09
{
class Program
{
static void Main(string[] args)
{
string basePath = Directory.GetCurrentDirectory();
string testFile = Path.Combine(basePath, "test.txt");
string configFile = Path.Combine(basePath, "config.json");
string logFile = Path.Combine(basePath, "app.log");
Console.WriteLine("=== 文件读写演示 ===\n");
// ========== 写入文本文件 ==========
Console.WriteLine("--- 写入文本文件 ---");
string content = "第一行:无言势字幕\n第二行VRChat 语音识别工具";
File.WriteAllText(testFile, content);
Console.WriteLine($"已写入: {testFile}");
// ========== 读取文本文件 ==========
Console.WriteLine("\n--- 读取文本文件 ---");
string readContent = File.ReadAllText(testFile);
Console.WriteLine("文件内容:");
Console.WriteLine(readContent);
// ========== 按行读写 ==========
Console.WriteLine("\n--- 按行读取 ---");
string[] lines = File.ReadAllLines(testFile);
for (int i = 0; i < lines.Length; i++)
{
Console.WriteLine($"行 {i + 1}: {lines[i]}");
}
// ========== JSON 配置 ==========
Console.WriteLine("\n--- JSON 配置读写 ---");
// 创建设置对象
AppSettings settings = new AppSettings
{
Sensitivity = 75,
AutoConnect = true,
VadEnabled = true,
Language = "zh-CN"
};
// 序列化为 JSON
string json = JsonSerializer.Serialize(settings, new JsonSerializerOptions
{
WriteIndented = true // 格式化输出
});
File.WriteAllText(configFile, json);
Console.WriteLine("已保存配置: config.json");
Console.WriteLine(json);
// 反序列化
string loadedJson = File.ReadAllText(configFile);
AppSettings loadedSettings = JsonSerializer.Deserialize<AppSettings>(loadedJson);
Console.WriteLine("\n读取的配置");
Console.WriteLine($" 灵敏度: {loadedSettings.Sensitivity}");
Console.WriteLine($" 自动连接: {loadedSettings.AutoConnect}");
Console.WriteLine($" VAD: {loadedSettings.VadEnabled}");
Console.WriteLine($" 语言: {loadedSettings.Language}");
// ========== 追加日志 ==========
Console.WriteLine("\n--- 追加日志 ---");
string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
string logEntry = $"[{timestamp}] 应用启动\n";
File.AppendAllText(logFile, logEntry);
Console.WriteLine($"已追加日志: {logFile}");
// ========== 文件操作 ==========
Console.WriteLine("\n--- 文件操作 ---");
Console.WriteLine($"文件是否存在: {File.Exists(testFile)}");
Console.WriteLine($"文件大小: {new FileInfo(testFile).Length} 字节");
Console.WriteLine($"完整路径: {Path.GetFullPath(testFile)}");
Console.WriteLine($"扩展名: {Path.GetExtension(testFile)}");
// ========== 清理 ==========
Console.WriteLine("\n--- 清理测试文件 ---");
if (File.Exists(testFile)) File.Delete(testFile);
if (File.Exists(configFile)) File.Delete(configFile);
if (File.Exists(logFile)) File.Delete(logFile);
Console.WriteLine("已删除测试文件");
Console.WriteLine("\n=== 演示完成 ===");
Console.ReadLine();
}
}
// ========== 配置类 ==========
class AppSettings
{
public int Sensitivity { get; set; } = 70;
public bool AutoConnect { get; set; } = true;
public bool VadEnabled { get; set; } = true;
public string Language { get; set; } = "zh-CN";
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

136
lessons/Lesson10/Program.cs Normal file
View File

@ -0,0 +1,136 @@
using System;
namespace Lesson10
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("=== 委托和事件演示 ===\n");
// ========== 委托 ==========
Console.WriteLine("--- 委托 ---");
// Action: 无返回值的委托
Action<string> print = (msg) => Console.WriteLine($"打印: {msg}");
print("Hello World");
// Func: 有返回值的委托
Func<int, int, int> add = (a, b) => a + b;
Func<int, int, int> multiply = (a, b) => a * b;
Console.WriteLine($"add(3, 5) = {add(3, 5)}");
Console.WriteLine($"multiply(3, 5) = {multiply(3, 5)}");
// ========== 事件 ==========
Console.WriteLine("\n--- 事件 ---");
Microphone mic = new Microphone("USB麦克风");
// 订阅事件 - 收到文字时
mic.OnTranscriptReady += (text) =>
{
Console.WriteLine($"[UI更新] 显示文字: {text}");
};
// 订阅事件 - 开始监听
mic.OnListeningStarted += () =>
{
Console.WriteLine("[UI更新] 开始播放动画");
};
// 订阅事件 - 停止监听
mic.OnListeningStopped += () =>
{
Console.WriteLine("[UI更新] 停止动画");
};
// 订阅事件 - 错误处理
mic.OnError += (error) =>
{
Console.WriteLine($"[UI更新] 显示错误: {error}");
};
// ========== 模拟使用流程 ==========
Console.WriteLine("\n--- 模拟识别流程 ---");
// 正常流程
mic.StartListening();
mic.TranscriptDetected("今天天气真不错");
mic.TranscriptDetected("大家一起加油");
mic.StopListening();
Console.WriteLine();
// 模拟错误
mic.StartListening();
mic.SimulateError("设备断开连接");
mic.StopListening();
Console.WriteLine("\n=== 演示完成 ===");
Console.ReadLine();
}
}
// ========== 麦克风类(带事件) ==========
class Microphone
{
private string name;
private bool isListening;
// 事件声明
public event Action<string> OnTranscriptReady; // 识别出文字
public event Action OnListeningStarted; // 开始监听
public event Action OnListeningStopped; // 停止监听
public event Action<string> OnError; // 错误
public Microphone(string name)
{
this.name = name;
this.isListening = false;
}
public void StartListening()
{
if (isListening)
{
OnError?.Invoke("已经在监听中");
return;
}
isListening = true;
Console.WriteLine($"[{name}] 开始监听");
OnListeningStarted?.Invoke(); // 通知所有订阅者
}
public void StopListening()
{
if (!isListening)
{
return;
}
isListening = false;
Console.WriteLine($"[{name}] 停止监听");
OnListeningStopped?.Invoke(); // 通知所有订阅者
}
public void TranscriptDetected(string text)
{
if (!isListening)
{
return;
}
Console.WriteLine($"[{name}] 识别: {text}");
OnTranscriptReady?.Invoke(text); // 通知所有订阅者
}
// 模拟错误
public void SimulateError(string error)
{
Console.WriteLine($"[{name}] 错误: {error}");
OnError?.Invoke(error);
}
}
}

View File

@ -0,0 +1,24 @@
// Lesson 01: Hello World
// 目标:熟悉 C# 基本结构和输出
using System;
namespace CSharpLearning
{
class Lesson01_HelloWorld
{
static void Main(string[] args)
{
// 打印 hello world
Console.WriteLine("Hello, World!");
Console.WriteLine("你好C#!");
// 打印带格式
string name = "无言势字幕";
Console.WriteLine($"项目名称: {name}");
// 等待输入(防止窗口关闭)
Console.ReadLine();
}
}
}

View File

@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,67 @@
using System;
namespace Lesson_One
{
class Program
{
static void Main(string[] args)
{
// ========== 变量 ==========
int age = 25;
double height = 175.5;
string name = "无言势字幕";
char grade = 'A';
bool isActive = true;
Console.WriteLine("=== 变量 ===");
Console.WriteLine($"姓名: {name}");
Console.WriteLine($"年龄: {age}");
Console.WriteLine($"身高: {height}cm");
Console.WriteLine($"等级: {grade}");
Console.WriteLine($"状态: {isActive}");
// ========== var 自动推断 ==========
Console.WriteLine("\n=== var 自动推断 ===");
var message = "这是 string 类型";
var number = 42; // 推断为 int
Console.WriteLine($"message 是: {message.GetType()}");
Console.WriteLine($"number 是: {number.GetType()}");
// ========== 常量 ==========
Console.WriteLine("\n=== 常量 ===");
const int MAX_RETRY = 3;
const string VERSION = "v1.0";
Console.WriteLine($"最大重试次数: {MAX_RETRY}");
Console.WriteLine($"版本: {VERSION}");
// ========== 类型转换 ==========
Console.WriteLine("\n=== 类型转换 ===");
int i = 10;
double d = i; // 隐式转换
Console.WriteLine($"int -> double: {d}");
double d2 = 3.14;
int i2 = (int)d2; // 显式转换
Console.WriteLine($"double -> int: {i2}");
string s = i.ToString();
Console.WriteLine($"int -> string: {s}");
int i3 = int.Parse("123");
Console.WriteLine($"string \"123\" -> int: {i3}");
// ========== 输入练习 ==========
Console.WriteLine("\n=== 输入练习 ===");
Console.Write("请输入你的名字: ");
string inputName = Console.ReadLine();
Console.Write($"你好, {inputName}! ");
Console.Write("请输入年龄: ");
string ageInput = Console.ReadLine();
int userAge = int.Parse(ageInput);
Console.WriteLine($"你 {userAge} 岁了");
Console.ReadLine();
}
}
}

46
notes/01_CSharp_Basics.md Normal file
View File

@ -0,0 +1,46 @@
# C# 基础入门
## 环境确认
- .NET SDK: `dotnet --version`
- 编辑器: VS Code + C# Dev Kit (可选) 或 Rider
## 常用命令
```bash
# 创建新项目
dotnet new console -n MyProject
# 运行
dotnet run
# 构建发布
dotnet publish -c Release
```
## 基本结构
```csharp
using System;
namespace MyProject
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}
```
## 关键概念
| 概念 | 说明 |
|------|------|
| `using` | 引入命名空间,类似 Python import |
| `namespace` | 命名空间,防止命名冲突 |
| `class` | 类,一切皆对象 |
| `static void Main` | 程序入口点 |
| `Console.WriteLine` | 输出到终端 |

View File

@ -0,0 +1,62 @@
# 第二课:变量和数据类型
## 基本数据类型
| 类型 | 说明 | 示例 |
|------|------|------|
| `int` | 整数 | `int age = 25;` |
| `double` | 小数 | `double pi = 3.14;` |
| `string` | 字符串 | `string name = "无言势";` |
| `char` | 单字符 | `char grade = 'A';` |
| `bool` | 布尔值 | `bool isActive = true;` |
| `var` | 自动推断 | `var msg = "自动推断为string";` |
## 声明和赋值
```csharp
// 声明 + 赋值
int score = 100;
// 先声明,后赋值
int score;
score = 100;
// 多个变量
int a = 1, b = 2, c = 3;
```
## 常量
```csharp
const double PI = 3.14159; // const 声明后不可修改
```
## 类型转换
```csharp
// 隐式转换(自动)
int i = 10;
double d = i; // int -> double
// 显式转换(强制)
double d = 3.14;
int i = (int)d; // double -> int结果是 3
// ToString / Parse
int i = int.Parse("123");
string s = i.ToString();
```
## 输入输出
```csharp
// 输出
Console.WriteLine("Hello"); // 换行
Console.Write("Hello"); // 不换行
// 输入(返回 string
string name = Console.ReadLine();
// 输入数字要转换
int age = int.Parse(Console.ReadLine());
```

View File

@ -0,0 +1,102 @@
# 第三课:条件判断和循环
## if / else 判断
```csharp
int score = 85;
if (score >= 90)
{
Console.WriteLine("优秀");
}
else if (score >= 60)
{
Console.WriteLine("及格");
}
else
{
Console.WriteLine("不及格");
}
// 三元运算符(简单判断)
string result = score >= 60 ? "及格" : "不及格";
```
## switch 选择
```csharp
int day = 3;
switch (day)
{
case 1:
Console.WriteLine("星期一");
break;
case 2:
Console.WriteLine("星期二");
break;
case 3:
Console.WriteLine("星期三");
break;
default:
Console.WriteLine("其他");
break;
}
```
## for 循环
```csharp
// 传统 for
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i);
}
// 遍历数组
string[] names = { "A", "B", "C" };
for (int i = 0; i < names.Length; i++)
{
Console.WriteLine(names[i]);
}
```
## foreach 循环(更简洁)
```csharp
string[] names = { "A", "B", "C" };
foreach (string name in names)
{
Console.WriteLine(name);
}
```
## while 循环
```csharp
int i = 0;
while (i < 5)
{
Console.WriteLine(i);
i++;
}
// 先执行再判断
do
{
Console.WriteLine(i);
i++;
} while (i < 5);
```
## break 和 continue
```csharp
for (int i = 0; i < 10; i++)
{
if (i == 3) continue; // 跳过 i=3
if (i == 7) break; // 退出循环
Console.WriteLine(i);
}
```

View File

@ -0,0 +1,91 @@
# 第四课:方法(函数)和类
## 方法(函数)
```csharp
// 无返回值
void SayHello(string name)
{
Console.WriteLine($"你好, {name}");
}
// 有返回值
int Add(int a, int b)
{
return a + b;
}
// 带默认参数
void Greet(string name = "World")
{
Console.WriteLine($"Hello, {name}");
}
```
## 方法调用
```csharp
static void Main(string[] args)
{
SayHello("无言势"); // 调用无返回值方法
int sum = Add(1, 2); // 调用有返回值方法
Console.WriteLine(sum); // 输出 3
}
```
## 类Class
```csharp
class Person
{
// 字段(属性)
public string name;
public int age;
// 构造函数
public Person(string name, int age)
{
this.name = name;
this.age = age;
}
// 方法
public void SayHi()
{
Console.WriteLine($"我是{name},今年{age}岁");
}
}
```
## 创建对象
```csharp
static void Main(string[] args)
{
Person p = new Person("无言势", 25);
p.SayHi();
}
```
## 访问修饰符
| 修饰符 | 访问范围 |
|--------|----------|
| `public` | 任何地方都能访问 |
| `private` | 只有本类内部能访问 |
| `internal` | 同程序集内访问 |
## 静态static
```csharp
class MathHelper
{
public static int Add(int a, int b)
{
return a + b;
}
}
// 调用不需要 new
int result = MathHelper.Add(1, 2);
```

View File

@ -0,0 +1,114 @@
# 第五课:多文件项目和访问修饰符
## 多文件项目结构
```
Lesson05/
├── Program.cs // 入口,只管 Main
├── Models/
│ ├── Person.cs // 数据模型
│ └── Microphone.cs // 麦克风类
├── Helpers/
│ └── MathHelper.cs // 工具类
└── Services/
└── AudioService.cs // 服务类
```
**关键规则**
- 同一项目内,所有 `.cs` 文件自动互相可见(不需要 import
- 用文件夹组织,但编译时所有文件合并成一个程序集
## public vs private 核心区别
| 修饰符 | 类内访问 | 类外访问 | 继承访问 |
|--------|----------|----------|----------|
| `public` | ✓ | ✓ | ✓ |
| `private` | ✓ | ✗ | ✗ |
```csharp
class Person
{
public string name; // 任何地方都能读写
private int age; // 只有 Person 内部能访问
private string secret; // 外部无法访问
public void SetAge(int age)
{
// 公有方法内部可以访问私有字段
if (age > 0 && age < 150)
this.age = age;
}
public int GetAge()
{
return this.age; // 通过方法间接访问
}
}
```
```csharp
// Program.cs
Person p = new Person("无言势");
p.name = "新名字"; // OKpublic
p.age = 25; // 编译错误private
p.SetAge(25); // OK通过公有方法
```
## 为什么要 private
**数据保护**:防止无效值
```csharp
private int age;
// 外部不能直接 age = -100
// 必须通过方法验证后设置
public void SetAge(int age)
{
if (age < 0) age = 0;
if (age > 150) age = 150;
this.age = age;
}
```
**封装**:内部实现可以随时改,不影响外部
```csharp
// 内部存储从 int 改成 DateTime 都可以
// 只要 public 方法签名不变,外部代码不用改
```
## 其他访问修饰符
| 修饰符 | 说明 |
|--------|------|
| `public` | 完全公开 |
| `private` | 仅本类 |
| `protected` | 本类 + 子类 |
| `internal` | 同项目内 |
## 属性Property- 介于 public/private 之间
```csharp
class Person
{
private int _age;
public int Age
{
get { return _age; } // 读取
set
{
if (value < 0) value = 0; // 写入时验证
if (value > 150) value = 150;
_age = value;
}
}
}
```
```csharp
Person p = new Person();
p.Age = 25; // 自动调用 set
int a = p.Age; // 自动调用 get
```

View File

@ -0,0 +1,191 @@
# 第六课:继承和接口
## 继承Inheritance
子类继承父类的属性和方法,避免重复代码。
```csharp
// 父类
class Animal
{
public string name;
public void Eat()
{
Console.WriteLine($"{name} 在吃东西");
}
}
// 子类
class Dog : Animal
{
public void Bark()
{
Console.WriteLine($"{name} 在叫: 汪汪!");
}
}
```
```csharp
Dog dog = new Dog();
dog.name = "旺财";
dog.Eat(); // 继承自 Animal
dog.Bark(); // 自己独有的
```
## 方法重写override
```csharp
// 父类 virtual 方法
class Animal
{
public string name;
public virtual void Speak()
{
Console.WriteLine($"{name} 发出了声音");
}
}
// 子类重写
class Cat : Animal
{
public override void Speak()
{
Console.WriteLine($"{name} 喵喵喵~");
}
}
```
```csharp
Animal a = new Cat();
a.name = "小猫";
a.Speak(); // 输出: 小猫 喵喵喵~
```
## base 关键字
子类调用父类的方法/构造函数:
```csharp
class Dog : Animal
{
public string breed;
public Dog(string name, string breed) : base(name)
{
this.breed = breed;
}
public override void Speak()
{
base.Speak(); // 先调用父类方法
Console.WriteLine($"{name} 汪汪!");
}
}
```
## 访问修饰符 protected
| 修饰符 | 任何地方 | 继承访问 |
|--------|----------|----------|
| `private` | ✗ | ✗ |
| `protected` | ✗ | ✓ |
```csharp
class Animal
{
protected string name; // private 的话子类也访问不到
}
class Dog : Animal
{
public void Test()
{
name = "旺财"; // protected 可以访问
}
}
```
## 接口Interface
接口 = 约定/契约,定义必须实现的方法。
```csharp
// 定义接口
interface IFlyable
{
void Fly(); // 不写实现
int MaxAltitude { get; } // 可以有属性
}
// 实现接口
class Bird : IFlyable
{
public int MaxAltitude => 1000;
public void Fly()
{
Console.WriteLine("鸟儿在飞翔");
}
}
class Plane : IFlyable
{
public int MaxAltitude => 10000;
public void Fly()
{
Console.WriteLine("飞机在飞翔");
}
}
```
## 接口 vs 继承
| | 继承 | 接口 |
|---|---|---|
| 关键词 | `class A : B` | `class A : IB` |
| 只能继承一个父类 | ✓ | 可以实现多个接口 |
| 继承获得代码 | ✓ | 只获得"约定" |
| 何时用 | 代码复用 | 约定行为 |
```csharp
// 一个类只能继承一个父类
class Dog : Animal { }
// 但可以实现多个接口
class Duck : Animal, IFlyable, ISwimable
{
public void Fly() { }
public void Swim() { }
}
```
## 例子:设备驱动架构
```csharp
// 设备接口
interface IDevice
{
void Connect();
void Disconnect();
bool IsConnected { get; }
}
// 麦克风设备
class Microphone : IDevice
{
public bool IsConnected { get; private set; }
public void Connect()
{
Console.WriteLine("麦克风连接");
IsConnected = true;
}
public void Disconnect()
{
Console.WriteLine("麦克风断开");
IsConnected = false;
}
}
```

135
notes/07_Collections.md Normal file
View File

@ -0,0 +1,135 @@
# 第七课集合List / Dictionary
## List<T> 动态数组
`T` 是泛型,占位符,使用时替换成具体类型。
```csharp
using System.Collections.Generic;
// 声明
List<string> names = new List<string>();
// 添加
names.Add("无言势");
names.Add("字幕");
names.Add("工具");
// 插入(指定位置)
names.Insert(1, "新插入");
// 删除
names.Remove("字幕");
names.RemoveAt(0); // 按索引删除
// 访问
string first = names[0];
// 数量
int count = names.Count;
// 遍历
foreach (string name in names)
{
Console.WriteLine(name);
}
```
## 初始化器
```csharp
// 声明时直接填充
List<string> fruits = new List<string>()
{
"苹果", "香蕉", "橙子"
};
// 简写C# 12+
List<string> fruits2 = ["苹果", "香蕉", "橙子"];
```
## 常用方法
```csharp
List<int> numbers = new List<int>() { 3, 1, 4, 1, 5 };
numbers.Sort(); // 排序
numbers.Reverse(); // 反转
numbers.Contains(5); // 是否包含
numbers.IndexOf(4); // 查找索引
numbers.Clear(); // 清空
numbers.InsertRange(0, ...); // 插入集合
```
## Dictionary<K, V> 键值对
```csharp
using System.Collections.Generic;
// 声明
Dictionary<string, int> scores = new Dictionary<string, int>();
// 添加
scores["无言势"] = 100;
scores["玩家A"] = 85;
scores.Add("玩家B", 90);
// 访问
int score = scores["无言势"]; // 100
// 安全访问(键不存在不报错)
scores.TryGetValue("不存在", out int value);
// 是否包含键
if (scores.ContainsKey("无言势"))
{
Console.WriteLine(scores["无言势"]);
}
// 删除
scores.Remove("玩家A");
// 遍历
foreach (KeyValuePair<string, int> kv in scores)
{
Console.WriteLine($"{kv.Key}: {kv.Value}");
}
// 只遍历键或值
foreach (string key in scores.Keys) { }
foreach (int value in scores.Values) { }
```
## Dictionary 初始化
```csharp
// C# 6+
Dictionary<string, int> config = new Dictionary<string, int>()
{
["sensitivity"] = 70,
["volume"] = 80,
["language"] = 1
};
```
## List 和 Dictionary 在项目中的应用
```csharp
// 识别结果历史
List<string> transcriptHistory = new List<string>();
// 设备配置
Dictionary<string, string> deviceConfig = new Dictionary<string, string>()
{
["name"] = "USB麦克风",
["sampleRate"] = "16000",
["channels"] = "1"
};
// 设置选项
Dictionary<string, bool> settings = new Dictionary<string, bool>()
{
["autoConnect"] = true,
["vad"] = true
};
```

View File

@ -0,0 +1,164 @@
# 第八课:异常处理
## 什么是异常
异常 = 程序运行时的错误,导致程序无法正常继续。
```csharp
int[] numbers = { 1, 2, 3 };
int x = numbers[10]; // 索引越界 → 抛出 IndexOutOfRangeException
```
## try / catch / finally
```csharp
try
{
// 可能出错的代码放这里
int[] numbers = { 1, 2, 3 };
int x = numbers[10];
}
catch (Exception ex)
{
// 捕获异常并处理
Console.WriteLine($"出错了: {ex.Message}");
}
finally
{
// 无论是否出错,都会执行(通常放清理代码)
Console.WriteLine("无论怎样都会执行");
}
```
## 捕获特定异常类型
```csharp
try
{
int a = int.Parse("abc"); // 格式错误
}
catch (FormatException ex)
{
Console.WriteLine("格式错误");
}
catch (OverflowException ex)
{
Console.WriteLine("数字溢出");
}
catch (Exception ex)
{
Console.WriteLine($"未知错误: {ex.Message}");
}
```
## 常见异常类型
| 异常类型 | 触发场景 |
|----------|----------|
| `FormatException` | 格式错误(`int.Parse("abc")` |
| `NullReferenceException` | 空对象访问(`obj.Method()``obj` 为 null |
| `IndexOutOfRangeException` | 数组越界 |
| `FileNotFoundException` | 文件不存在 |
| `DivideByZeroException` | 除数为零 |
| `InvalidOperationException` | 操作无效状态 |
## throw 抛出异常
```csharp
static void SetAge(int age)
{
if (age < 0 || age > 150)
{
throw new ArgumentException("年龄必须在 0-150 之间");
}
Console.WriteLine($"年龄设置为: {age}");
}
```
```csharp
try
{
SetAge(-5);
}
catch (ArgumentException ex)
{
Console.WriteLine($"参数错误: {ex.Message}");
}
```
## 自定义异常
```csharp
class MicrophoneException : Exception
{
public MicrophoneException(string message) : base(message) { }
public MicrophoneException(string message, Exception inner) : base(message, inner) { }
}
```
## finally 的作用
```csharp
FileStream file = null;
try
{
file = new FileStream("test.txt", FileMode.Open);
// 读取文件...
}
catch (FileNotFoundException ex)
{
Console.WriteLine("文件不存在");
}
finally
{
// 确保文件被关闭
file?.Close();
}
```
## using 语句(更简洁)
```csharp
// using 自动释放资源(相当于 try-finally
using (FileStream file = new FileStream("test.txt", FileMode.Open))
{
// 使用 file
} // 作用域结束后自动 Close()
```
## 实际项目应用
```csharp
try
{
// 尝试连接麦克风
microphone.Connect();
}
catch (DeviceNotFoundException ex)
{
Console.WriteLine("麦克风未找到,请检查连接");
ShowReconnectDialog();
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine("没有权限访问麦克风");
RequestPermission();
}
catch (Exception ex)
{
Console.WriteLine($"未知错误: {ex.Message}");
LogError(ex);
}
```
## 不要过度捕获异常
```csharp
// ✗ 不好:捕获所有异常但不处理
try { DoSomething(); }
catch { }
// ✓ 好:只捕获能处理的异常
try { Connect(); }
catch (DeviceNotFoundException ex) { ShowError(); }
```

150
notes/09_File_IO.md Normal file
View File

@ -0,0 +1,150 @@
# 第九课:文件读写
## 基本文件操作
```csharp
using System.IO;
// 写入文本
File.WriteAllText("test.txt", "你好,无言势字幕");
// 读取文本
string content = File.ReadAllText("test.txt");
```
## 路径操作
```csharp
// 路径拼接
string path = Path.Combine("C:", "Users", "test", "config.json");
// 获取扩展名
string ext = Path.GetExtension("test.txt"); // ".txt"
// 获取文件名
string name = Path.GetFileName("C:/test/test.txt"); // "test.txt"
// 判断文件是否存在
bool exists = File.Exists("config.json");
```
## 读取文件
```csharp
// 方式1一次性读取适合小文件
string content = File.ReadAllText("test.txt");
// 方式2按行读取
string[] lines = File.ReadAllLines("test.txt");
foreach (string line in lines)
{
Console.WriteLine(line);
}
// 方式3流式读取大文件
using (StreamReader reader = new StreamReader("test.txt"))
{
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
Console.WriteLine(line);
}
}
```
## 写入文件
```csharp
// 方式1一次性写入
File.WriteAllText("output.txt", "内容");
// 方式2按行写入
string[] lines = { "第一行", "第二行", "第三行" };
File.WriteAllLines("output.txt", lines);
// 方式3追加内容
File.AppendAllText("log.txt", "新追加的内容\n");
// 方式4流式写入
using (StreamWriter writer = new StreamWriter("output.txt"))
{
writer.WriteLine("第一行");
writer.WriteLine("第二行");
}
```
## JSON 序列化
.NET 内置 JSON 支持System.Text.Json
```csharp
using System.Text.Json;
using System.Text.Json.Serialization;
// 序列化(对象 -> JSON
class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
Person p = new Person { Name = "无言势", Age = 25 };
string json = JsonSerializer.Serialize(p);
Console.WriteLine(json);
// 输出: {"Name":"无言势","Age":25}
// 反序列化JSON -> 对象)
string jsonStr = "{\"Name\":\"玩家A\",\"Age\":30}";
Person p2 = JsonSerializer.Deserialize<Person>(jsonStr);
Console.WriteLine(p2.Name); // "玩家A"
```
## 配置文件示例
```csharp
// AppSettings 类
class AppSettings
{
public int Sensitivity { get; set; } = 70;
public bool AutoConnect { get; set; } = true;
public bool VadEnabled { get; set; } = true;
public string Language { get; set; } = "zh-CN";
}
// 保存配置
AppSettings settings = new AppSettings();
string json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText("config.json", json);
// 读取配置
string loaded = File.ReadAllText("config.json");
AppSettings loadedSettings = JsonSerializer.Deserialize<AppSettings>(loaded);
```
## using 语句自动关闭
```csharp
// StreamReader/Writer 必须关闭,否则文件被锁定
using (StreamReader reader = new StreamReader("test.txt"))
{
string line = reader.ReadLine();
}
// 作用域结束后自动 Close()
```
## 常见错误处理
```csharp
try
{
string content = File.ReadAllText("notexist.txt");
}
catch (FileNotFoundException)
{
Console.WriteLine("文件不存在");
}
catch (IOException ex)
{
Console.WriteLine($"IO错误: {ex.Message}");
}
```

View File

@ -0,0 +1,182 @@
# 第十课:委托和事件
## 委托Delegate是什么
委托 = 函数的"容器",可以把函数当作变量传递。
```csharp
// 声明委托类型
delegate int Calculator(int a, int b);
// 定义符合委托的方法
int Add(int a, int b) => a + b;
int Multiply(int a, int b) => a * b;
// 使用委托
Calculator calc = Add;
int result = calc(1, 2); // result = 3
calc = Multiply;
result = calc(3, 4); // result = 12
```
## 为什么需要委托
```csharp
// 场景:数组遍历时对每个元素做操作
void ProcessNumbers(int[] numbers, ??? operation)
{
foreach (int n in numbers)
{
??? result = operation(n);
Console.WriteLine(result);
}
}
// 没有委托,你需要写多个重载
// 有委托,可以传任意操作
ProcessNumbers(nums, x => x * 2); // 翻倍
ProcessNumbers(nums, x => x + 1); // +1
ProcessNumbers(nums, x => x * x); // 平方
```
## Action 和 Func简化委托
```csharp
// Action: 返回 void 的委托
Action<string> print = (msg) => Console.WriteLine(msg);
print("Hello"); // 无返回值
// Func: 有返回值的委托
Func<int, int, int> add = (a, b) => a + b;
int result = add(1, 2); // 3
// 多参数
Func<string, string, string> concat = (a, b) => a + b;
string s = concat("Hello", " World"); // "Hello World"
```
## 事件Event是什么
事件 = 委托的升级版,只能 += 添加监听,不能外部直接调用。
```csharp
class Button
{
// 事件声明
public event Action Clicked;
public void OnClick()
{
Console.WriteLine("按钮被点击");
// 触发事件(只能在类内部)
Clicked?.Invoke();
}
}
```
```csharp
// 使用事件
Button btn = new Button();
// 添加监听者
btn.Clicked += () => Console.WriteLine("Handler 1 执行");
btn.Clicked += () => Console.WriteLine("Handler 2 执行");
btn.OnClick();
// 输出:
// 按钮被点击
// Handler 1 执行
// Handler 2 执行
```
## 事件 + 委托的应用场景
```csharp
class Microphone
{
// 事件声明
public event Action<string> OnTranscriptReady; // 识别出文字时触发
public event Action OnListeningStarted;
public event Action OnListeningStopped;
private bool isListening;
public void StartListening()
{
isListening = true;
Console.WriteLine("麦克风开始监听...");
OnListeningStarted?.Invoke();
}
public void StopListening()
{
isListening = false;
Console.WriteLine("麦克风停止监听...");
OnListeningStopped?.Invoke();
}
// 模拟识别到文字
public void TranscriptDetected(string text)
{
Console.WriteLine($"识别到: {text}");
OnTranscriptReady?.Invoke(text); // 触发事件,通知所有监听者
}
}
```
```csharp
// 使用
Microphone mic = new Microphone();
// 订阅事件
mic.OnTranscriptReady += (text) =>
{
Console.WriteLine($"收到文字: {text}");
};
mic.OnListeningStarted += () =>
{
Console.WriteLine("开始记录...");
};
mic.OnListeningStopped += () =>
{
Console.WriteLine("停止记录");
};
// 触发
mic.StartListening();
mic.TranscriptDetected("今天天气不错");
mic.TranscriptDetected("大家一起加油");
mic.StopListening();
```
## 事件 vs 委托
| | 委托 | 事件 |
|---|---|---|
| 外部添加监听 | ✓ | ✓ |
| 外部调用 | ✓ | ✗ |
| 主要用途 | 传递函数 | 通知机制 |
## 移除监听
```csharp
Action handler = () => Console.WriteLine("Handler");
btn.Clicked += handler;
btn.Clicked -= handler; // 移除
```
## 实际项目结构
```
语音识别服务
├── OnTranscriptReady(text) → UI 更新文字
├── OnListeningStarted() → UI 显示监听状态
├── OnListeningStopped() → UI 停止动画
└── OnError(error) → UI 显示错误
```
事件让各部分解耦,不需要互相引用。

121
notes/11_WebView2_Basics.md Normal file
View File

@ -0,0 +1,121 @@
# WebView2 入门
## 什么是 WebView2
在 Windows 桌面应用里嵌入 Chrome 内核浏览器。
```
┌─────────────────────────┐
│ WPF / WinForms │
│ ┌───────────────────┐ │
│ │ WebView2 │ │
│ │ (Chrome 内核) │ │
│ │ │ │
│ │ ┌─────────────┐ │ │
│ │ │ HTML/CSS/JS │ │ │
│ │ │ 你的界面 │ │ │
│ │ └─────────────┘ │ │
│ └───────────────────┘ │
│ │
│ C# 代码 │
└─────────────────────────┘
```
## 安装 WebView2 Runtime
下载地址https://developer.microsoft.com/en-us/microsoft-edge/webview2/
## NuGet 包
```bash
dotnet add package Microsoft.Web.WebView2
```
## XAML 使用
```xml
<Window xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf">
<Grid>
<wv2:WebView2 x:Name="WebView"
Source="https://example.com"/>
</Grid>
</Window>
```
## 初始化
```csharp
private async void InitializeWebView()
{
await WebView.EnsureCoreWebView2Async();
Console.WriteLine("WebView2 初始化成功");
}
```
## C# 调用 JS
```csharp
// 执行 JS
await WebView.ExecuteScriptAsync("alert('Hello from C#')");
// 获取返回值
string result = await WebView.ExecuteScriptAsync("document.title");
```
## JS 调用 C#
```csharp
// C# 注册方法
WebView.CoreWebView2.WebMessageReceived += (s, e) =>
{
string message = e.TryGetWebMessageAsString();
Console.WriteLine($"收到JS消息: {message}");
};
```
```javascript
// JS 发送消息
window.chrome.webview.postMessage("Hello from JS");
```
## 常用操作
```csharp
WebView.Reload(); // 刷新
WebView.GoBack(); // 后退
WebView.GoForward(); // 前进
WebView.Navigate("https://..."); // 导航到 URL
WebView.CoreWebView2.Settings // 配置
```
## 本地 HTML
```csharp
// 加载本地 HTML 文件
string htmlPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "index.html");
WebView.CoreWebView2.Navigate(new Uri(htmlPath).AbsoluteUri);
// 或者直接加载 HTML 字符串
string html = "<h1>Hello</h1>";
WebView.NavigateToString(html);
```
## 项目里怎么用
```
无言势字幕/
├── MainWindow.xaml ← WPF 窗口,放 WebView2
├── index.html ← 你的界面HTML/CSS/JS
├── WebViewManager.cs ← C# 和 JS 通信
└── AsrService.cs ← 语音识别服务
```
C# 负责:
- 语音识别Sherpa-ONNX
- OSC 发送
- 窗口管理
HTML/JS 负责:
- 界面显示
- 动画效果
- 用户交互

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.