commit f604d5319156c60d09fef25b258824ffe28774b6 Author: Rosmontis_Cloud <2451830085@qq.com> Date: Wed Jul 1 16:31:35 2026 +0800 Initial commit: C# 学习笔记和示例代码 - Lesson 01-10: C# 基础语法 - WebView2: 集成示例 - notes/: 详细笔记 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3cf4ff9 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/WebView2Demo b/WebView2Demo new file mode 160000 index 0000000..1d85cb1 --- /dev/null +++ b/WebView2Demo @@ -0,0 +1 @@ +Subproject commit 1d85cb160f81d5a41125d348f97c01b2db0a09b4 diff --git a/lessons/Lesson02/Lesson02.csproj b/lessons/Lesson02/Lesson02.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/lessons/Lesson02/Lesson02.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/lessons/Lesson02/Program.cs b/lessons/Lesson02/Program.cs new file mode 100644 index 0000000..5a0173b --- /dev/null +++ b/lessons/Lesson02/Program.cs @@ -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(); + } + } +} diff --git a/lessons/Lesson03/Lesson03.csproj b/lessons/Lesson03/Lesson03.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/lessons/Lesson03/Lesson03.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/lessons/Lesson03/Program.cs b/lessons/Lesson03/Program.cs new file mode 100644 index 0000000..1531250 --- /dev/null +++ b/lessons/Lesson03/Program.cs @@ -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(); + } + } +} diff --git a/lessons/Lesson04/Lesson04.csproj b/lessons/Lesson04/Lesson04.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/lessons/Lesson04/Lesson04.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/lessons/Lesson04/Program.cs b/lessons/Lesson04/Program.cs new file mode 100644 index 0000000..3b7c7bd --- /dev/null +++ b/lessons/Lesson04/Program.cs @@ -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}] 停止监听"); + } + } + } +} diff --git a/lessons/Lesson05/Helpers/MathHelper.cs b/lessons/Lesson05/Helpers/MathHelper.cs new file mode 100644 index 0000000..b8b81ad --- /dev/null +++ b/lessons/Lesson05/Helpers/MathHelper.cs @@ -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; + } + } +} diff --git a/lessons/Lesson05/Lesson05.csproj b/lessons/Lesson05/Lesson05.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/lessons/Lesson05/Lesson05.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/lessons/Lesson05/Models/Person.cs b/lessons/Lesson05/Models/Person.cs new file mode 100644 index 0000000..ce1327a --- /dev/null +++ b/lessons/Lesson05/Models/Person.cs @@ -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; + } + } +} diff --git a/lessons/Lesson05/Program.cs b/lessons/Lesson05/Program.cs new file mode 100644 index 0000000..c9b8e5f --- /dev/null +++ b/lessons/Lesson05/Program.cs @@ -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(); + } + } +} diff --git a/lessons/Lesson05/Services/AudioService.cs b/lessons/Lesson05/Services/AudioService.cs new file mode 100644 index 0000000..2500521 --- /dev/null +++ b/lessons/Lesson05/Services/AudioService.cs @@ -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; + } + } +} diff --git a/lessons/Lesson06/Animals/Animal.cs b/lessons/Lesson06/Animals/Animal.cs new file mode 100644 index 0000000..7d7319a --- /dev/null +++ b/lessons/Lesson06/Animals/Animal.cs @@ -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} 发出了声音"); + } + } +} diff --git a/lessons/Lesson06/Animals/Cat.cs b/lessons/Lesson06/Animals/Cat.cs new file mode 100644 index 0000000..8808970 --- /dev/null +++ b/lessons/Lesson06/Animals/Cat.cs @@ -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} 喵喵喵~"); + } + } +} diff --git a/lessons/Lesson06/Animals/Dog.cs b/lessons/Lesson06/Animals/Dog.cs new file mode 100644 index 0000000..8cbe59b --- /dev/null +++ b/lessons/Lesson06/Animals/Dog.cs @@ -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} 汪汪汪!"); + } + } +} diff --git a/lessons/Lesson06/Devices/Camera.cs b/lessons/Lesson06/Devices/Camera.cs new file mode 100644 index 0000000..6a585dc --- /dev/null +++ b/lessons/Lesson06/Devices/Camera.cs @@ -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}] 摄像头已断开"); + } + } + } +} diff --git a/lessons/Lesson06/Devices/IDevice.cs b/lessons/Lesson06/Devices/IDevice.cs new file mode 100644 index 0000000..e448f41 --- /dev/null +++ b/lessons/Lesson06/Devices/IDevice.cs @@ -0,0 +1,10 @@ +namespace Lesson06.Devices +{ + // 接口:定义契约 + interface IDevice + { + void Connect(); + void Disconnect(); + bool IsConnected { get; } + } +} diff --git a/lessons/Lesson06/Devices/Microphone.cs b/lessons/Lesson06/Devices/Microphone.cs new file mode 100644 index 0000000..068ebf5 --- /dev/null +++ b/lessons/Lesson06/Devices/Microphone.cs @@ -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}] 麦克风已断开"); + } + } + } +} diff --git a/lessons/Lesson06/Lesson06.csproj b/lessons/Lesson06/Lesson06.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/lessons/Lesson06/Lesson06.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/lessons/Lesson06/Program.cs b/lessons/Lesson06/Program.cs new file mode 100644 index 0000000..010b699 --- /dev/null +++ b/lessons/Lesson06/Program.cs @@ -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(); + } + } +} diff --git a/lessons/Lesson07/Lesson07.csproj b/lessons/Lesson07/Lesson07.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/lessons/Lesson07/Lesson07.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/lessons/Lesson07/Program.cs b/lessons/Lesson07/Program.cs new file mode 100644 index 0000000..889078e --- /dev/null +++ b/lessons/Lesson07/Program.cs @@ -0,0 +1,107 @@ +using System; +using System.Collections.Generic; + +namespace Lesson07 +{ + class Program + { + static void Main(string[] args) + { + // ========== List ========== + Console.WriteLine("=== List 动态数组 ===\n"); + + List transcriptHistory = new List(); + + // 添加识别结果 + 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 fruits = new List() { "苹果", "香蕉", "橙子" }; + Console.WriteLine($"\n水果列表:{string.Join(", ", fruits)}"); + + // ========== Dictionary ========== + Console.WriteLine("\n=== Dictionary 键值对 ===\n"); + + // 设备配置 + Dictionary deviceConfig = new Dictionary() + { + ["name"] = "USB麦克风", + ["sampleRate"] = "16000", + ["channels"] = "1" + }; + + Console.WriteLine("设备配置:"); + foreach (KeyValuePair 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 settings = new Dictionary() + { + ["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 scores = new List() { 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(); + } + } +} diff --git a/lessons/Lesson08/Lesson08.csproj b/lessons/Lesson08/Lesson08.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/lessons/Lesson08/Lesson08.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/lessons/Lesson08/Program.cs b/lessons/Lesson08/Program.cs new file mode 100644 index 0000000..24f7df1 --- /dev/null +++ b/lessons/Lesson08/Program.cs @@ -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; + } + } +} diff --git a/lessons/Lesson09/Lesson09.csproj b/lessons/Lesson09/Lesson09.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/lessons/Lesson09/Lesson09.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/lessons/Lesson09/Program.cs b/lessons/Lesson09/Program.cs new file mode 100644 index 0000000..89be971 --- /dev/null +++ b/lessons/Lesson09/Program.cs @@ -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(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"; + } +} diff --git a/lessons/Lesson10/Lesson10.csproj b/lessons/Lesson10/Lesson10.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/lessons/Lesson10/Lesson10.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/lessons/Lesson10/Program.cs b/lessons/Lesson10/Program.cs new file mode 100644 index 0000000..4b5c2bf --- /dev/null +++ b/lessons/Lesson10/Program.cs @@ -0,0 +1,136 @@ +using System; + +namespace Lesson10 +{ + class Program + { + static void Main(string[] args) + { + Console.WriteLine("=== 委托和事件演示 ===\n"); + + // ========== 委托 ========== + Console.WriteLine("--- 委托 ---"); + + // Action: 无返回值的委托 + Action print = (msg) => Console.WriteLine($"打印: {msg}"); + print("Hello World"); + + // Func: 有返回值的委托 + Func add = (a, b) => a + b; + Func 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 OnTranscriptReady; // 识别出文字 + public event Action OnListeningStarted; // 开始监听 + public event Action OnListeningStopped; // 停止监听 + public event Action 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); + } + } +} diff --git a/lessons/Lesson_One/Lesson01_HelloWorld.cs b/lessons/Lesson_One/Lesson01_HelloWorld.cs new file mode 100644 index 0000000..4b8a2d6 --- /dev/null +++ b/lessons/Lesson_One/Lesson01_HelloWorld.cs @@ -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(); + } + } +} diff --git a/lessons/Lesson_One/Lesson_One.csproj b/lessons/Lesson_One/Lesson_One.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/lessons/Lesson_One/Lesson_One.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/lessons/Lesson_One/Program.cs b/lessons/Lesson_One/Program.cs new file mode 100644 index 0000000..d8a32d8 --- /dev/null +++ b/lessons/Lesson_One/Program.cs @@ -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(); + } + } +} diff --git a/notes/01_CSharp_Basics.md b/notes/01_CSharp_Basics.md new file mode 100644 index 0000000..fe94937 --- /dev/null +++ b/notes/01_CSharp_Basics.md @@ -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` | 输出到终端 | diff --git a/notes/02_Variables_and_Types.md b/notes/02_Variables_and_Types.md new file mode 100644 index 0000000..ba0be51 --- /dev/null +++ b/notes/02_Variables_and_Types.md @@ -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()); +``` diff --git a/notes/03_Conditions_and_Loops.md b/notes/03_Conditions_and_Loops.md new file mode 100644 index 0000000..8354106 --- /dev/null +++ b/notes/03_Conditions_and_Loops.md @@ -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); +} +``` diff --git a/notes/04_Methods_and_Classes.md b/notes/04_Methods_and_Classes.md new file mode 100644 index 0000000..65452bf --- /dev/null +++ b/notes/04_Methods_and_Classes.md @@ -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); +``` diff --git a/notes/05_MultiFile_and_Access_Modifiers.md b/notes/05_MultiFile_and_Access_Modifiers.md new file mode 100644 index 0000000..86f671c --- /dev/null +++ b/notes/05_MultiFile_and_Access_Modifiers.md @@ -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 = "新名字"; // OK,public +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 +``` diff --git a/notes/06_Inheritance_and_Interface.md b/notes/06_Inheritance_and_Interface.md new file mode 100644 index 0000000..21fc35d --- /dev/null +++ b/notes/06_Inheritance_and_Interface.md @@ -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; + } +} +``` diff --git a/notes/07_Collections.md b/notes/07_Collections.md new file mode 100644 index 0000000..f6323cd --- /dev/null +++ b/notes/07_Collections.md @@ -0,0 +1,135 @@ +# 第七课:集合(List / Dictionary) + +## List 动态数组 + +`T` 是泛型,占位符,使用时替换成具体类型。 + +```csharp +using System.Collections.Generic; + +// 声明 +List names = new List(); + +// 添加 +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 fruits = new List() +{ + "苹果", "香蕉", "橙子" +}; + +// 简写(C# 12+) +List fruits2 = ["苹果", "香蕉", "橙子"]; +``` + +## 常用方法 + +```csharp +List numbers = new List() { 3, 1, 4, 1, 5 }; + +numbers.Sort(); // 排序 +numbers.Reverse(); // 反转 +numbers.Contains(5); // 是否包含 +numbers.IndexOf(4); // 查找索引 +numbers.Clear(); // 清空 +numbers.InsertRange(0, ...); // 插入集合 +``` + +## Dictionary 键值对 + +```csharp +using System.Collections.Generic; + +// 声明 +Dictionary scores = new Dictionary(); + +// 添加 +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 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 config = new Dictionary() +{ + ["sensitivity"] = 70, + ["volume"] = 80, + ["language"] = 1 +}; +``` + +## List 和 Dictionary 在项目中的应用 + +```csharp +// 识别结果历史 +List transcriptHistory = new List(); + +// 设备配置 +Dictionary deviceConfig = new Dictionary() +{ + ["name"] = "USB麦克风", + ["sampleRate"] = "16000", + ["channels"] = "1" +}; + +// 设置选项 +Dictionary settings = new Dictionary() +{ + ["autoConnect"] = true, + ["vad"] = true +}; +``` diff --git a/notes/08_Exception_Handling.md b/notes/08_Exception_Handling.md new file mode 100644 index 0000000..b1553fb --- /dev/null +++ b/notes/08_Exception_Handling.md @@ -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(); } +``` diff --git a/notes/09_File_IO.md b/notes/09_File_IO.md new file mode 100644 index 0000000..45eaeeb --- /dev/null +++ b/notes/09_File_IO.md @@ -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(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(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}"); +} +``` diff --git a/notes/10_Delegates_and_Events.md b/notes/10_Delegates_and_Events.md new file mode 100644 index 0000000..7046f33 --- /dev/null +++ b/notes/10_Delegates_and_Events.md @@ -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 print = (msg) => Console.WriteLine(msg); +print("Hello"); // 无返回值 + +// Func: 有返回值的委托 +Func add = (a, b) => a + b; +int result = add(1, 2); // 3 + +// 多参数 +Func 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 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 显示错误 +``` + +事件让各部分解耦,不需要互相引用。 diff --git a/notes/11_WebView2_Basics.md b/notes/11_WebView2_Basics.md new file mode 100644 index 0000000..0010f00 --- /dev/null +++ b/notes/11_WebView2_Basics.md @@ -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 + + + + + +``` + +## 初始化 + +```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 = "

Hello

"; +WebView.NavigateToString(html); +``` + +## 项目里怎么用 + +``` +无言势字幕/ +├── MainWindow.xaml ← WPF 窗口,放 WebView2 +├── index.html ← 你的界面(HTML/CSS/JS) +├── WebViewManager.cs ← C# 和 JS 通信 +└── AsrService.cs ← 语音识别服务 +``` + +C# 负责: +- 语音识别(Sherpa-ONNX) +- OSC 发送 +- 窗口管理 + +HTML/JS 负责: +- 界面显示 +- 动画效果 +- 用户交互 diff --git a/notes/PDF/01_CSharp_Basics.pdf b/notes/PDF/01_CSharp_Basics.pdf new file mode 100644 index 0000000..5c9aa60 Binary files /dev/null and b/notes/PDF/01_CSharp_Basics.pdf differ diff --git a/notes/PDF/02_Variables_and_Types.pdf b/notes/PDF/02_Variables_and_Types.pdf new file mode 100644 index 0000000..2022458 Binary files /dev/null and b/notes/PDF/02_Variables_and_Types.pdf differ diff --git a/notes/PDF/03_Conditions_and_Loops.pdf b/notes/PDF/03_Conditions_and_Loops.pdf new file mode 100644 index 0000000..a9b93ee Binary files /dev/null and b/notes/PDF/03_Conditions_and_Loops.pdf differ diff --git a/notes/PDF/04_Methods_and_Classes.pdf b/notes/PDF/04_Methods_and_Classes.pdf new file mode 100644 index 0000000..89580fb Binary files /dev/null and b/notes/PDF/04_Methods_and_Classes.pdf differ diff --git a/notes/PDF/05_MultiFile_and_Access_Modifiers.pdf b/notes/PDF/05_MultiFile_and_Access_Modifiers.pdf new file mode 100644 index 0000000..cd5cf75 Binary files /dev/null and b/notes/PDF/05_MultiFile_and_Access_Modifiers.pdf differ diff --git a/notes/PDF/06_Inheritance_and_Interface.pdf b/notes/PDF/06_Inheritance_and_Interface.pdf new file mode 100644 index 0000000..50e53fa Binary files /dev/null and b/notes/PDF/06_Inheritance_and_Interface.pdf differ diff --git a/notes/PDF/07_Collections.pdf b/notes/PDF/07_Collections.pdf new file mode 100644 index 0000000..aac49b3 Binary files /dev/null and b/notes/PDF/07_Collections.pdf differ diff --git a/notes/PDF/08_Exception_Handling.pdf b/notes/PDF/08_Exception_Handling.pdf new file mode 100644 index 0000000..359551a Binary files /dev/null and b/notes/PDF/08_Exception_Handling.pdf differ