【Tools】C#文件自动生成UML图
1. 必要条件:
安装Java环境、安装Graphviz环境:
安装VS Code,在VS Code里安装PlantUML插件、Graphviz Preview插件:
编写convert_cs_to_puml.py文件,将其放在与cs文件放在同一个文件夹:
import os import redef parse_cs_file(content):"""解析单个C#文件的内容,提取类名、字段、属性、方法、继承关系和依赖。支持静态字段、静态方法、属性、方法参数类型依赖、字段初始化、继承关系。"""class_info = {'name': None,'fields': [],'methods': [],'dependencies': set(),'inherits': None # 基类}# 提取类名及继承class_match = re.search(r'class\s+([A-Za-z0-9_]+)(\s*:\s*([A-Za-z0-9_<>]+))?', content)if not class_match:return Noneclass_info['name'] = class_match.group(1)if class_match.group(3):class_info['inherits'] = class_match.group(3)class_info['dependencies'].add(class_match.group(3))# 提取字段(支持可选 static 和初始化表达式)field_pattern = re.compile(r'\b(public|private|protected)\s+(static\s+)?([A-Za-z0-9_<>?]+)\s+([A-Za-z0-9_]+)(?:\s*=\s*[^;]+)?\s*;')for match in field_pattern.finditer(content):visibility = match.group(1)is_static = match.group(2)data_type = match.group(3)variable_name = match.group(4)# 添加依赖if data_type not in ['int', 'string', 'bool', 'double', 'float', 'void', 'DateTime']:class_info['dependencies'].add(data_type.replace('?', '').replace('[]', ''))puml_symbol = get_puml_symbol(visibility)field_str = f' {puml_symbol}{variable_name} : {data_type}'if is_static:field_str = '{static} ' + field_strclass_info['fields'].append(field_str)# 提取属性(Property)property_pattern = re.compile(r'\b(public|private|protected)\s+(static\s+)?([A-Za-z0-9_<>?]+)\s+([A-Za-z0-9_]+)\s*\{\s*get;\s*set;\s*\}')for match in property_pattern.finditer(content):visibility = match.group(1)is_static = match.group(2)data_type = match.group(3)property_name = match.group(4)if data_type not in ['int', 'string', 'bool', 'double', 'float', 'void', 'DateTime']:class_info['dependencies'].add(data_type.replace('?', '').replace('[]', ''))puml_symbol = get_puml_symbol(visibility)prop_str = f' {puml_symbol}{property_name} : {data_type} {{property}}'if is_static:prop_str = '{static} ' + prop_strclass_info['fields'].append(prop_str)# 提取方法及参数类型(支持 static 和多行参数)method_pattern = re.compile(r'\b(public|private|protected)\s+(static\s+)?([A-Za-z0-9_<>?]+)\s+([A-Za-z0-9_]+)\s*\((.*?)\)\s*{?',re.DOTALL)for match in method_pattern.finditer(content):visibility = match.group(1)is_static = match.group(2)return_type = match.group(3)method_name = match.group(4)params = match.group(5).replace('\n',' ').strip() # 去掉换行# 返回类型依赖if return_type not in ['int', 'string', 'bool', 'double', 'float', 'void', 'DateTime']:class_info['dependencies'].add(return_type.replace('?', '').replace('[]', ''))# 参数类型依赖if params:param_list = [p.strip() for p in params.split(',')]for p in param_list:parts = p.split()if len(parts) >= 2:param_type = parts[0]if param_type not in ['int', 'string', 'bool', 'double', 'float', 'void', 'DateTime']:class_info['dependencies'].add(param_type.replace('?', '').replace('[]', ''))puml_symbol = get_puml_symbol(visibility)method_str = f' {puml_symbol}{method_name}({params}) : {return_type}'if is_static:method_str = '{static} ' + method_strclass_info['methods'].append(method_str)return class_infodef get_puml_symbol(visibility):if visibility == 'public':return '+'elif visibility == 'private':return '-'elif visibility == 'protected':return '#'return ''def generate_puml_for_single_class(class_info):if not class_info:return Nonepuml_content = ['@startuml', '', 'skinparam classAttributeIconSize 0']# 类成员puml_content.append(f'class {class_info["name"]} {{')puml_content.extend(class_info['fields'])if class_info['fields'] and class_info['methods']:puml_content.append('')puml_content.extend(class_info['methods'])puml_content.append('}')# 继承关系if class_info['inherits']:puml_content.append(f'{class_info["name"]} --|> {class_info["inherits"]}')puml_content.append('')puml_content.append('@enduml')return '\n'.join(puml_content)def process_and_generate_multiple_files():script_dir = os.path.dirname(os.path.abspath(__file__))found_cs_files = Falsefor filename in os.listdir(script_dir):if filename.endswith('.cs'):found_cs_files = Truefile_path = os.path.join(script_dir, filename)print(f"正在解析文件: {filename}")try:with open(file_path, 'r', encoding='utf-8') as f:content = f.read()class_info = parse_cs_file(content)if class_info:puml_content = generate_puml_for_single_class(class_info)puml_filename = filename.replace('.cs', '.puml')output_file_path = os.path.join(script_dir, puml_filename)with open(output_file_path, 'w', encoding='utf-8') as f:f.write(puml_content)print(f"成功生成 UML 图文件: {puml_filename}")else:print(f"文件 {filename} 中未找到有效的 C# 类定义。")except Exception as e:print(f"解析 {filename} 失败: {e}")if not found_cs_files:print("未找到任何C#文件。")if __name__ == '__main__':process_and_generate_multiple_files()
编写完上面这个文件后,假设现在在同级文件夹下添加一个A.cs,文件内容如下:
public class A {public int a = 1; }
运行convert_cs_to_puml.py文件,可以在同级文件夹下看到A.puml:
@startumlskinparam classAttributeIconSize 0 class A {+a : int }@enduml
最后Alt + D,生成UML图: