diff --git a/Test2.txt b/Test2.txt index 2942a1b..dcedd00 100644 --- a/Test2.txt +++ b/Test2.txt @@ -1,366 +1,71 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Text.RegularExpressions; -using NPOI.SS.UserModel; -using NPOI.XSSF.UserModel; -using UnityEngine; -using UnityEditor; -using UnityEditor.Compilation; +--- +name: "high-quality-coding" +description: "高质量编程规范 - 涵盖改动影响分析、逻辑完整性、注释、性能优化、可读性和内存安全六大维度的编码最佳实践" +--- -public class ExcelToEntityGenerator : EditorWindow -{ - private static string ExcelFolder = "Assets/Data/Excels"; - private static string ScriptsFolder = "Assets/Scripts/Data"; - private static string AssetFolder = "Assets/Data/Containers"; +# 高质量编程规范 (High-Quality Coding Standards) - public static void LoadAll(string excelFolder, string scriptsFolder, string assetFolder) - { - ExcelFolder = excelFolder; - ScriptsFolder = scriptsFolder; - AssetFolder = assetFolder; - // 确保输出目录存在 - if (!Directory.Exists(ScriptsFolder)) Directory.CreateDirectory(ScriptsFolder); - if (!Directory.Exists(AssetFolder)) Directory.CreateDirectory(AssetFolder); +你是一位追求代码质量的资深软件工程师。在编写和修改代码时,你必须严格遵循以下规范。 - // 加载所有 Excel 文件 - var allData = ExcelBatchLoader.LoadAllXlsxFiles(ExcelFolder); - if (allData.Count == 0) - { - Debug.LogWarning("未找到任何 .xlsx 文件"); - return; - } +## 1. 改动影响分析 (Change Impact Analysis) - // 为每个文件生成数据行类和容器类 - foreach (var kv in allData) - { - string fileName = kv.Key; // 不含扩展名 - var sheet = kv.Value[0]; // 只处理第一个工作表 +**每次编辑代码前**,先分析改动的影响范围: - // 解析字段信息 - IRow nameRow = sheet.GetRow(0); - IRow typeRow = sheet.GetRow(1); - List fields = ParseFieldInfo(nameRow, typeRow); +- **调用链追踪**:找出所有调用被修改函数的位置,逐一确认参数类型、返回值处理是否兼容。如果函数签名发生变化,必须更新所有调用方。 +- **依赖关系检查**:确认被修改的变量、类型、接口是否被其他模块引用。全局变量和共享状态的修改需要格外谨慎。 +- **边界条件验证**:修改逻辑后,检查边界输入(null、空数组、零值、负数、超长字符串等)是否仍然被正确处理。 +- **错误路径覆盖**:确保修改后的代码在异常路径(网络超时、文件不存在、权限不足等)下不会崩溃或产生未定义行为。 +- **并发安全**:如果代码涉及多线程/异步操作,确认修改后的数据访问仍然是线程安全的,不会引入竞态条件。 - // 生成数据行类 - string rowClassPath = $"{ScriptsFolder}/{fileName}Container.cs"; - var rowClassContent = GenerateRowClass(fields, fileName, rowClassPath); +**编辑完成后**,在心中模拟执行修改涉及的至少三条关键路径(正常路径、边界路径、异常路径)。 - // 生成容器类 - string containerClassPath = $"{ScriptsFolder}/{fileName}Container.cs"; - GenerateContainerClass(fileName, containerClassPath, rowClassContent); - } - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - } +## 2. 功能逻辑完整性 (Logical Integrity) - public static void CreateAllContainers() - { - // 重新加载 Excel 文件(此时数据行类已编译) - var allData = ExcelBatchLoader.LoadAllXlsxFiles(ExcelFolder); - foreach (var kv in allData) - { - string fileName = kv.Key; - var sheet = kv.Value[0]; - CreateContainerFromSheet(sheet, fileName); - } - AssetDatabase.SaveAssets(); - AssetDatabase.Refresh(); - Debug.Log("所有 ScriptableObject 容器已生成完毕"); - } +- **状态一致性**:任何操作在失败时应回滚到操作前的状态,不允许出现"半完成"的中间状态。使用事务、try-catch-finally 或 defer 来保证清理逻辑一定执行。 +- **幂等性考虑**:对于可能被重复调用的函数(如事件处理器、API 接口),确保重复执行不会造成数据重复或状态错乱。 +- **输入校验**:所有外部输入(用户输入、API 响应、文件内容)在使用前必须校验。不要信任任何来自函数外部的数据。 +- **返回值处理**:每个函数调用后都应检查返回值/错误码。忽略错误返回必须有明确的理由并用注释说明。 +- **避免隐式行为**:不要依赖隐式类型转换、隐式全局状态或隐式执行顺序。所有行为都应该是显式的、可预测的。 - private static List ParseFieldInfo(IRow nameRow, IRow typeRow) - { - List fields = new List(); - for (int i = 0; i < nameRow.LastCellNum; i++) - { - string rawName = nameRow.GetCell(i)?.ToString()?.Trim(); - string rawType = typeRow.GetCell(i)?.ToString()?.Trim(); - if (string.IsNullOrEmpty(rawName) || string.IsNullOrEmpty(rawType)) - continue; +## 3. 注释规范 (Comment Standards) - string fieldName = SanitizeName(rawName); - string fieldType = MapType(rawType); // 映射为C#类型字符串 - fields.Add(new FieldInfo { Name = fieldName, RawType = rawType, Type = fieldType }); - } - return fields; - } +以下位置**必须**添加注释: - private static string SanitizeName(string name) - { - // 移除非法字符,并确保首字母大写 - string sanitized = Regex.Replace(name, @"[^a-zA-Z0-9_]", ""); - if (string.IsNullOrEmpty(sanitized)) return "Field"; - return char.ToUpper(sanitized[0]) + sanitized.Substring(1); - } +- **公开函数/方法**:说明功能、参数含义、返回值含义、可能抛出的异常。 +- **复杂算法**:用简短的注释解释算法思路,标注关键步骤。如果算法来自已知来源(论文、博客),注明出处。 +- **非直观的逻辑**:任何"看起来奇怪但有意为之"的代码必须有注释解释 why,否则会被后续维护者当作 bug 改掉。 +- **业务规则**:涉及业务逻辑的阈值、条件判断、特殊处理,必须注释其业务含义。 +- **TODO/FIXME/HACK**:临时方案必须标注原因和期望的修复时间。 - private static string MapType(string typeStr) - { - switch (typeStr.ToLower()) - { - case "int": return "int"; - case "float": return "float"; - case "bool": return "bool"; - case "string": return "string"; - case "intlist": return "List"; - case "floatlist": return "List"; - case "boollist": return "List"; - case "stringlist": return "List"; - default: return "string"; - } - } +注释应回答"为什么这样做"(why),而非复述代码"做了什么"(what)。 - private static string GenerateRowClass(List fields, string className, string outputPath) - { - StringBuilder sb = new StringBuilder(); - sb.AppendLine("//由程序自动生成"); - sb.AppendLine("using System;"); - sb.AppendLine("using System.Collections.Generic;"); - sb.AppendLine("using UnityEngine;"); - sb.AppendLine(); - sb.AppendLine("[System.Serializable]"); - sb.AppendLine($"public class {className}"); - sb.AppendLine("{"); +## 4. 性能优化 (Performance Optimization) - foreach (var field in fields) - { - sb.AppendLine($" public {field.Type} {field.Name};"); - } +- **避免不必要的分配**:在循环内创建对象、在热路径上分配大数组是常见的性能杀手。尽量复用对象、使用对象池或预分配。 +- **选择合适的算法和数据结构**:根据数据规模选择 O 复杂度合适的方案。对于查找密集的场景,优先考虑 Map/Set 而非 Array;对于频繁插入删除,优先考虑链表而非数组。 +- **惰性求值**:昂贵计算在真正需要时才执行。使用缓存(memoization)避免重复计算。 +- **异步非阻塞**:I/O 操作(文件读写、网络请求、数据库查询)应使用异步方式,避免阻塞主线程或事件循环。 +- **批量操作**:数据库查询、网络请求、DOM 更新应尽量批量处理,减少往返次数。 +- **避免过早优化**:先保证正确性,再针对经过性能分析(profiling)确认的热点进行优化。但不要写出明显低效的代码(如 O(n²) 的嵌套循环在已知数据会很大时)。 - sb.AppendLine("}"); +## 5. 代码可读性 (Code Readability) - // WriteFile(outputPath, sb.ToString()); - return sb.ToString(); - } +- **单一职责**:每个函数只做一件事。如果函数超过 40 行(不含注释和空行),考虑拆分。 +- **命名清晰**:变量、函数、类名应准确描述其用途。避免单字母变量(循环索引 i、j 除外),避免缩写(除非是领域通用缩写如 `req`/`res`/`ctx`)。 +- **减少嵌套**:使用提前返回(early return)减少嵌套层级。理想的最大嵌套深度为 3 层。 +- **避免魔法数字**:所有非零非一的字面常量应提取为命名常量,并添加注释说明其含义和来源。 +- **一致的代码风格**:与项目现有风格保持一致,不引入新的风格变体。 - private static void GenerateContainerClass(string rowClassName, string outputPath, string rowClassContent) - { - - string containerName = rowClassName + "Container"; - StringBuilder sb = new StringBuilder(); - // sb.AppendLine("using System.Collections.Generic;"); - // sb.AppendLine("using UnityEngine;"); - sb.AppendLine(rowClassContent); - sb.AppendLine(); - sb.AppendLine("[CreateAssetMenu(fileName = \"" + containerName + "\", menuName = \"Data/" + containerName + "\")]"); - sb.AppendLine($"public class {containerName} : ScriptableObject"); - sb.AppendLine("{"); - sb.AppendLine($" public List<{rowClassName}> items;"); - sb.AppendLine("}"); +## 6. 内存与资源安全 (Memory & Resource Safety) - WriteFile(outputPath, sb.ToString()); - } +- **资源释放**:所有打开的资源(文件句柄、网络连接、数据库连接、锁、定时器、事件监听器)必须在不再使用时显式释放。使用 RAII、try-with-resources、defer 或 finally 块确保释放。 +- **避免循环引用**:在存在垃圾回收的语言中,注意闭包、事件监听器、回调函数持有对象引用导致的内存泄漏。及时解绑不再需要的监听器。 +- **缓存上限**:任何缓存机制必须有容量上限和淘汰策略。无限增长的缓存就是内存泄漏。 +- **大对象生命周期**:避免在全局作用域或长寿对象中持有大块数据的引用。处理大文件时应使用流式处理而非一次性加载。 +- **定时器清理**:所有 setInterval/setTimeout/定时任务在组件销毁或不再需要时必须清除。 +- **避免深拷贝滥用**:对大型数据结构频繁进行深拷贝会造成严重的性能问题和内存压力。优先使用不可变数据结构或结构共享。 - private static void WriteFile(string path, string content) - { - // 如果文件已存在,删除后重新创建(避免残留) - if (File.Exists(path)) - { - File.Delete(path); - } - File.WriteAllText(path, content, Encoding.UTF8); - } +--- - private static void CreateContainerFromSheet(ISheet sheet, string fileName) - { - try - { - // 获取已编译的类型 - string rowClassName = fileName; - string containerClassName = fileName + "Container"; - System.Reflection.Assembly assembly = System.Reflection.Assembly.Load("Assembly-CSharp"); - Type rowType = assembly.GetType(rowClassName); - Type containerType = assembly.GetType(containerClassName); - if (rowType == null || containerType == null) - { - Debug.LogError($"无法找到类型: {rowClassName} 或 {containerClassName},请检查编译是否成功"); - return; - } +在每次代码编辑操作中,主动应用以上规范。编辑完成后,简要自检:改动影响是否可控?逻辑是否完整?关键位置是否有注释?性能是否有退化?可读性是否良好?资源是否妥善管理? - // 解析字段信息(与生成类时一致) - IRow nameRow = sheet.GetRow(0); - IRow typeRow = sheet.GetRow(1); - List fields = ParseFieldInfo(nameRow, typeRow); - - // 创建容器实例 - ScriptableObject container = ScriptableObject.CreateInstance(containerType); - if (container == null) - { - Debug.LogError($"无法创建容器实例: {containerClassName}"); - return; - } - - // 获取 items 字段并创建 List - System.Reflection.FieldInfo itemsField = containerType.GetField("items"); - Type listType = typeof(List<>).MakeGenericType(rowType); - object listInstance = Activator.CreateInstance(listType); - - // 遍历数据行(从第3行开始,索引2) - for (int rowIdx = 2; rowIdx <= sheet.LastRowNum; rowIdx++) - { - IRow dataRow = sheet.GetRow(rowIdx); - if (dataRow == null) continue; - - // 创建数据行对象 - object rowObj = Activator.CreateInstance(rowType); - - bool hasData = false; // 标记该行是否有有效数据 - for (int i = 0; i < fields.Count; i++) - { - var field = fields[i]; - var cell = dataRow.GetCell(i); - string cellValue = cell?.ToString()?.Trim(); - if (string.IsNullOrEmpty(cellValue)) continue; - - hasData = true; - // 转换并赋值 - object convertedValue = ConvertValue(cellValue, field.RawType); - System.Reflection.FieldInfo rowField = rowType.GetField(field.Name); - if (rowField != null) - { - rowField.SetValue(rowObj, convertedValue); - } - } - - if (hasData) - { - // 添加到列表 - listType.GetMethod("Add").Invoke(listInstance, new[] { rowObj }); - } - } - - // 将列表赋值给容器 - itemsField.SetValue(container, listInstance); - - // 保存资产 - string assetPath = $"{AssetFolder}/{fileName}Container.asset"; - // 如果已存在,先删除 - if (File.Exists(assetPath)) - { - AssetDatabase.DeleteAsset(assetPath); - } - AssetDatabase.CreateAsset(container, assetPath); - Debug.Log($"生成容器资产: {assetPath}"); - } - catch (Exception e) - { - Debug.LogError($"处理文件 {fileName} 时出错: {e.Message}\n{e.StackTrace}"); - } - } - - private static object ConvertValue(string value, string rawType) - { - string lowerType = rawType.ToLower(); - try - { - switch (lowerType) - { - case "int": - return int.Parse(value); - case "float": - return float.Parse(value); - case "bool": - return bool.Parse(value); - case "string": - return value; - case "intlist": - return ParseList(value, int.Parse); - case "floatlist": - return ParseList(value, float.Parse); - case "boollist": - return ParseList(value, bool.Parse); - case "stringlist": - return ParseList(value, s => s); - default: - return value; - } - } - catch (Exception e) - { - Debug.LogWarning($"转换值 '{value}' 到类型 {rawType} 失败: {e.Message},使用默认值。"); - return GetDefaultValue(rawType); - } - } - - private static List ParseList(string value, Func parser) - { - List list = new List(); - if (string.IsNullOrEmpty(value)) return list; - string[] parts = value.Split(new[] { '#' }, StringSplitOptions.RemoveEmptyEntries); - foreach (string part in parts) - { - try - { - list.Add(parser(part.Trim())); - } - catch - { - // 忽略解析失败的项 - } - } - return list; - } - - private static object GetDefaultValue(string rawType) - { - switch (rawType.ToLower()) - { - case "int": return 0; - case "float": return 0f; - case "bool": return false; - case "string": return ""; - case "intlist": return new List(); - case "floatlist": return new List(); - case "boollist": return new List(); - case "stringlist": return new List(); - default: return null; - } - } - - private class FieldInfo - { - public string Name; - public string RawType; // Excel中的原始类型标识 - public string Type; // 映射后的C#类型字符串 - } -} - -public static class ExcelBatchLoader -{ - public static Dictionary> LoadAllXlsxFiles(string directoryPath, SearchOption searchOption = SearchOption.TopDirectoryOnly) - { - var result = new Dictionary>(); - - if (!Directory.Exists(directoryPath)) - { - Debug.LogError($"目录不存在: {directoryPath}"); - return result; - } - - string[] files = Directory.GetFiles(directoryPath, "*.xlsx", searchOption); - foreach (string filePath in files) - { - try - { - using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read)) - { - IWorkbook workbook = new XSSFWorkbook(fs); - List sheets = new List(); - for (int i = 0; i < workbook.NumberOfSheets; i++) - { - sheets.Add(workbook.GetSheetAt(i)); - } - var fileName = Path.GetFileNameWithoutExtension(filePath); - result[fileName] = sheets; - } - } - catch (Exception e) - { - Debug.LogError($"加载文件失败: {filePath}\n{e.Message}"); - } - } - - return result; - } -} \ No newline at end of file