编辑
2025-09-22
C#
00

目录

💔 传统配置管理的痛点分析
✨ 源代码生成器:编译时的魔法
🎯 我们的解决方案架构
🔧 第一步:定义特性标注
🏗️ 第二步:构建源代码生成器核心
🎨 第三步:智能代码生成逻辑
🚀 第四步:使用体验如丝般顺滑
🎯 实战应用场景
📊 数据库配置
📧 邮箱服务配置
完整代码
项目结构
ConfigurationLib.SourceGenerators
ConfigurationLib
示例调用
对应配置文件
⚠️ 开发中的坑点提醒
💡 进阶优化技巧
🔥 支持复杂类型
🎛️ 环境变量支持
🎉 总结:拥抱编译时编程

记得前面写过一篇关于用反射实现的,这篇是用网友们推荐的Sources Generators技术实现的,效率上确实会好不少,就是代码阅读性会上升一些。你是否厌倦了在项目中手写大量的配置读取代码?每次新增配置项都要写一堆重复的JSON解析逻辑?今天我们来看看如何用C#源代码生成器(Source Generators)彻底解决这个痛点,让配置管理变得如丝般顺滑!

注意:这个方案算是抛转引玉,旨在上次反射方案的改进,实际项目中还没有使用过,其实这一切都是从Java spring引出来的。。。

💔 传统配置管理的痛点分析

作为.NET开发者,我们经常遇到这些头疼的问题:

手工劳动繁重:每个配置类都要写重复的读取逻辑

C#
// 传统方式:大量重复代码 var config = new ApiConfig(); if (jsonObject.TryGetProperty("baseUrl", out var baseUrlElement)) { config.BaseUrl = baseUrlElement.GetString(); } if (jsonObject.TryGetProperty("timeout", out var timeoutElement)) { config.Timeout = timeoutElement.GetInt32(); } // ... 更多重复代码

维护成本高:配置结构变化时,需要同步修改多个地方

容错性差:缺少统一的异常处理和默认值设定

开发效率低:每次都要写相似的模板代码

✨ 源代码生成器:编译时的魔法

C#源代码生成器是.NET 5引入的强大功能,它能在编译时自动生成代码。就像有个智能助手在你编码时自动帮你写重复的模板代码!

🎯 我们的解决方案架构

让我们构建一个完整的自动化配置管理系统:

🔧 第一步:定义特性标注

首先创建两个特性来标记我们的配置类和属性:

C#
[AttributeUsage(AttributeTargets.Class)] public class ConfigurationAttribute : Attribute { public string? FileName { get; set; } public string? Section { get; set; } public bool Required { get; set; } = true; } [AttributeUsage(AttributeTargets.Property)] public class ConfigPropertyAttribute : Attribute { public string? Key { get; set; } public object? DefaultValue { get; set; } public bool Required { get; set; } = false; }

🏗️ 第二步:构建源代码生成器核心

创建一个增量源代码生成器,这是性能最优的方式:

C#
[Generator] public class ConfigurationGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { // 查找标记了ConfigurationAttribute的类 var configClasses = context.SyntaxProvider .CreateSyntaxProvider( predicate: static (s, _) => IsSyntaxTargetForGeneration(s), transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx)) .Where(static m => m is not null); // 合并所有配置类信息并生成代码 var compilation = context.CompilationProvider.Combine(configClasses.Collect()); context.RegisterSourceOutput(compilation, static (spc, source) => Execute(source.Left, source.Right!, spc)); } }

关键技术点

  • 使用增量生成器提供最佳性能
  • 语法分析语义分析分离,提高效率
  • 静态方法避免闭包分配,减少GC压力

🎨 第三步:智能代码生成逻辑

生成器的核心是根据配置类自动生成加载逻辑:

C#
private static void GeneratePropertyAssignment(StringBuilder sb, ConfigurationPropertyInfo property) { sb.AppendLine($"if (rootElement.TryGetProperty(\"{property.ConfigKey}\", out var {property.Name.ToLower()}Element))"); sb.AppendLine("{"); // 根据属性类型生成不同的赋值代码 switch (property.Type) { case "string": sb.AppendLine($" config.{property.Name} = {property.Name.ToLower()}Element.GetString();"); break; case "int": sb.AppendLine($" config.{property.Name} = {property.Name.ToLower()}Element.GetInt32();"); break; // 更多类型支持... } sb.AppendLine("}"); // 处理默认值和必填验证 if (property.DefaultValue != null) { sb.AppendLine($"else {{ config.{property.Name} = {FormatDefaultValue(property.DefaultValue, property.Type)}; }}"); } }

🚀 第四步:使用体验如丝般顺滑

现在开发者只需要简单地标记配置类:

C#
[Configuration(FileName = "api", Required = true)] public class ApiConfig { [ConfigProperty(Key = "baseUrl", Required = true)] public string BaseUrl { get; set; } = string.Empty; [ConfigProperty(Key = "timeout", DefaultValue = 5000)] public int TimeoutMs { get; set; } [ConfigProperty(Key = "endpoints")] public string[]? Endpoints { get; set; } }

使用时只需一行代码:

C#
var configManager = new GeneratedConfigurationManager(); configManager.LoadConfigurations("config"); var apiConfig = configManager.GetConfiguration<ApiConfig>();

🎯 实战应用场景

📊 数据库配置

C#
[Configuration(FileName = "database", Section = "Database", Required = true)] public class DatabaseConfig { [ConfigProperty(Key = "connectionString", Required = true)] public string ConnectionString { get; set; } = string.Empty; [ConfigProperty(Key = "timeout", DefaultValue = 30)] public int CommandTimeout { get; set; } }

📧 邮箱服务配置

C#
[Configuration(FileName = "email", Section = "Email", Required = true)] public class EmailConfig { [ConfigProperty(Key = "host", Required = true)] public string Host { get; set; } [ConfigProperty(Key = "port", Required = true)] public int Port { get; set; } }

完整代码

项目结构

image.png

ConfigurationLib.SourceGenerators

C#
using System; using System.Collections.Generic; using System.Text; namespace ConfigurationLib.SourceGenerators.Models { public class ConfigurationClassInfo { public string ClassName { get; set; } public string Namespace { get; set; } public string? FileName { get; set; } public string? Section { get; set; } public bool Required { get; set; } public List<ConfigurationPropertyInfo> Properties { get; set; } = new(); } }
C#
using System; using System.Collections.Generic; using System.Text; namespace ConfigurationLib.SourceGenerators.Models { public class ConfigurationPropertyInfo { public string Name { get; set; } public string Type { get; set; } public string ConfigKey { get; set; } public object? DefaultValue { get; set; } public bool IsRequired { get; set; } } }
C#
using System; using System.Collections.Immutable; using System.Linq; using System.Text; using ConfigurationLib.SourceGenerators.Models; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; namespace ConfigurationLib.SourceGenerators { [Generator] public class ConfigurationGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { // 查找标记了ConfigurationAttribute的类 var configClasses = context.SyntaxProvider .CreateSyntaxProvider( predicate: static (s, _) => IsSyntaxTargetForGeneration(s), transform: static (ctx, _) => GetSemanticTargetForGeneration(ctx)) .Where(static m => m is not null); // 合并所有配置类信息 var compilation = context.CompilationProvider.Combine(configClasses.Collect()); // 生成代码 context.RegisterSourceOutput(compilation, static (spc, source) => Execute(source.Left, source.Right!, spc)); } private static bool IsSyntaxTargetForGeneration(SyntaxNode node) { return node is ClassDeclarationSyntax { AttributeLists.Count: > 0 }; } private static ConfigurationClassInfo? GetSemanticTargetForGeneration(GeneratorSyntaxContext context) { var classDeclaration = (ClassDeclarationSyntax)context.Node; var classSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclaration) as INamedTypeSymbol; if (classSymbol == null) return null; var configAttribute = classSymbol.GetAttributes() .FirstOrDefault(attr => attr.AttributeClass?.Name == "ConfigurationAttribute"); if (configAttribute == null) return null; var fileName = configAttribute.NamedArguments .FirstOrDefault(arg => arg.Key == "FileName").Value.Value?.ToString(); var section = configAttribute.NamedArguments .FirstOrDefault(arg => arg.Key == "Section").Value.Value?.ToString(); var required = configAttribute.NamedArguments .FirstOrDefault(arg => arg.Key == "Required").Value.Value as bool? ?? true; var properties = classSymbol.GetMembers() .OfType<IPropertySymbol>() .Where(p => p.CanBeReferencedByName && p.SetMethod != null) .Select(p => new ConfigurationPropertyInfo { Name = p.Name, Type = p.Type.ToDisplayString(), ConfigKey = GetConfigKey(p), DefaultValue = GetDefaultValue(p), IsRequired = GetIsRequired(p) }) .ToList(); return new ConfigurationClassInfo { ClassName = classSymbol.Name, Namespace = classSymbol.ContainingNamespace.ToDisplayString(), FileName = fileName, Section = section, Required = required, Properties = properties }; } private static string GetConfigKey(IPropertySymbol property) { var configPropAttribute = property.GetAttributes() .FirstOrDefault(attr => attr.AttributeClass?.Name == "ConfigPropertyAttribute"); var key = configPropAttribute?.NamedArguments .FirstOrDefault(arg => arg.Key == "Key").Value.Value?.ToString(); return key ?? property.Name; } private static object? GetDefaultValue(IPropertySymbol property) { var configPropAttribute = property.GetAttributes() .FirstOrDefault(attr => attr.AttributeClass?.Name == "ConfigPropertyAttribute"); return configPropAttribute?.NamedArguments .FirstOrDefault(arg => arg.Key == "DefaultValue").Value.Value; } private static bool GetIsRequired(IPropertySymbol property) { var configPropAttribute = property.GetAttributes() .FirstOrDefault(attr => attr.AttributeClass?.Name == "ConfigPropertyAttribute"); return configPropAttribute?.NamedArguments .FirstOrDefault(arg => arg.Key == "Required").Value.Value as bool? ?? false; } private static void Execute(Compilation compilation, ImmutableArray<ConfigurationClassInfo> configClasses, SourceProductionContext context) { if (configClasses.IsDefaultOrEmpty) return; var sourceBuilder = new StringBuilder(); // 生成头部 sourceBuilder.AppendLine("using System;"); sourceBuilder.AppendLine("using System.Collections.Generic;"); sourceBuilder.AppendLine("using System.IO;"); sourceBuilder.AppendLine("using System.Text.Json;"); sourceBuilder.AppendLine("using ConfigurationLib;"); sourceBuilder.AppendLine(); sourceBuilder.AppendLine("namespace ConfigurationLib.Generated"); sourceBuilder.AppendLine("{"); // 生成ConfigurationManager类 sourceBuilder.Append(GenerateConfigurationManager(configClasses)); // 生成每个配置类的加载器 foreach (var configClass in configClasses) { sourceBuilder.Append(GenerateConfigurationLoader(configClass)); } // 结束类和命名空间 sourceBuilder.AppendLine(" }"); // 结束 GeneratedConfigurationManager 类 sourceBuilder.AppendLine("}"); // 结束 namespace context.AddSource("GeneratedConfigurationManager.g.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8)); } private static string GenerateConfigurationManager(ImmutableArray<ConfigurationClassInfo> configClasses) { var sb = new StringBuilder(); sb.AppendLine(" public class GeneratedConfigurationManager : IConfigurationManager"); sb.AppendLine(" {"); sb.AppendLine(" private readonly Dictionary<Type, object> _configInstances = new();"); sb.AppendLine(" private readonly Dictionary<string, JsonDocument> _configFiles = new();"); sb.AppendLine(); sb.AppendLine(" public void LoadConfigurations(string configDirectory = \"config\")"); sb.AppendLine(" {"); sb.AppendLine(" LoadConfigFiles(configDirectory);"); sb.AppendLine(); // 为每个配置类生成加载代码 foreach (var configClass in configClasses) { sb.AppendLine($" Load{configClass.ClassName}Configuration();"); } sb.AppendLine(" }"); sb.AppendLine(); sb.AppendLine(" private void LoadConfigFiles(string configDirectory)"); sb.AppendLine(" {"); sb.AppendLine(" if (!Directory.Exists(configDirectory))"); sb.AppendLine(" return;"); sb.AppendLine(); sb.AppendLine(" var jsonFiles = Directory.GetFiles(configDirectory, \"*.json\", SearchOption.AllDirectories);"); sb.AppendLine(" foreach (var filePath in jsonFiles)"); sb.AppendLine(" {"); sb.AppendLine(" try"); sb.AppendLine(" {"); sb.AppendLine(" var fileName = Path.GetFileNameWithoutExtension(filePath);"); sb.AppendLine(" var jsonContent = File.ReadAllText(filePath);"); sb.AppendLine(" var jsonDocument = JsonDocument.Parse(jsonContent);"); sb.AppendLine(" _configFiles[fileName.ToLower()] = jsonDocument;"); sb.AppendLine(" }"); sb.AppendLine(" catch (Exception ex)"); sb.AppendLine(" {"); sb.AppendLine(" Console.WriteLine($\"Failed to load config file {filePath}: {ex.Message}\");"); sb.AppendLine(" }"); sb.AppendLine(" }"); sb.AppendLine(" }"); sb.AppendLine(); sb.AppendLine(" public T GetConfiguration<T>() where T : class, new()"); sb.AppendLine(" {"); sb.AppendLine(" return _configInstances.TryGetValue(typeof(T), out var instance) ? (T)instance : null;"); sb.AppendLine(" }"); sb.AppendLine(); sb.AppendLine(" public IEnumerable<object> GetAllConfigurations()"); sb.AppendLine(" {"); sb.AppendLine(" return _configInstances.Values;"); sb.AppendLine(" }"); sb.AppendLine(); return sb.ToString(); } private static string GenerateConfigurationLoader(ConfigurationClassInfo configClass) { var sb = new StringBuilder(); sb.AppendLine($" private void Load{configClass.ClassName}Configuration()"); sb.AppendLine(" {"); sb.AppendLine($" var config = new {configClass.Namespace}.{configClass.ClassName}();"); sb.AppendLine(); // 确定配置文件名 var fileNameExpression = configClass.FileName != null ? $"\"{configClass.FileName.ToLower()}\"" : $"\"{configClass.ClassName.ToLower()}\""; if (configClass.FileName == null) { sb.AppendLine($" var fileName = {fileNameExpression};"); sb.AppendLine(" if (fileName.EndsWith(\"config\"))"); sb.AppendLine(" fileName = fileName.Replace(\"config\", \"\");"); } else { sb.AppendLine($" var fileName = {fileNameExpression};"); } sb.AppendLine(); sb.AppendLine(" if (!_configFiles.TryGetValue(fileName, out var jsonDocument))"); sb.AppendLine(" {"); if (configClass.Required) { sb.AppendLine(" throw new FileNotFoundException($\"Required configuration file not found: {fileName}.json\");"); } else { sb.AppendLine(" _configInstances[typeof(" + configClass.Namespace + "." + configClass.ClassName + ")] = config;"); sb.AppendLine(" return;"); } sb.AppendLine(" }"); sb.AppendLine(); sb.AppendLine(" var rootElement = jsonDocument.RootElement;"); // 处理Section if (!string.IsNullOrEmpty(configClass.Section)) { sb.AppendLine($" if (rootElement.TryGetProperty(\"{configClass.Section}\", out var sectionElement))"); sb.AppendLine(" rootElement = sectionElement;"); if (configClass.Required) { sb.AppendLine(" else"); sb.AppendLine($" throw new InvalidOperationException($\"Required section '{configClass.Section}' not found in {{fileName}}.json\");"); } } sb.AppendLine(); // 为每个属性生成赋值代码 foreach (var property in configClass.Properties) { GeneratePropertyAssignment(sb, property); } sb.AppendLine(); sb.AppendLine($" _configInstances[typeof({configClass.Namespace}.{configClass.ClassName})] = config;"); sb.AppendLine(" }"); sb.AppendLine(); return sb.ToString(); } private static void GeneratePropertyAssignment(StringBuilder sb, ConfigurationPropertyInfo property) { sb.AppendLine($" // Setting property: {property.Name}"); sb.AppendLine($" if (rootElement.TryGetProperty(\"{property.ConfigKey}\", out var {property.Name.ToLower()}Element))"); sb.AppendLine(" {"); sb.AppendLine(" try"); sb.AppendLine(" {"); // 根据属性类型生成不同的赋值代码 GenerateTypeSpecificAssignment(sb, property); sb.AppendLine(" }"); sb.AppendLine(" catch (Exception ex)"); sb.AppendLine(" {"); sb.AppendLine($" Console.WriteLine($\"Error setting property {property.Name}: {{ex.Message}}\");"); sb.AppendLine(" }"); sb.AppendLine(" }"); // 处理默认值 if (property.DefaultValue != null) { sb.AppendLine(" else"); sb.AppendLine(" {"); sb.AppendLine($" config.{property.Name} = {FormatDefaultValue(property.DefaultValue, property.Type)};"); sb.AppendLine(" }"); } else if (property.IsRequired) { sb.AppendLine(" else"); sb.AppendLine(" {"); sb.AppendLine($" throw new InvalidOperationException($\"Required property '{property.ConfigKey}' not found in configuration\");"); sb.AppendLine(" }"); } sb.AppendLine(); } private static void GenerateTypeSpecificAssignment(StringBuilder sb, ConfigurationPropertyInfo property) { var elementVar = $"{property.Name.ToLower()}Element"; // 改进类型判断逻辑 var type = property.Type; var isNullable = type.EndsWith("?"); var baseType = isNullable ? type.Substring(0, type.Length - 1) : type; switch (baseType) { case "string": sb.AppendLine($" config.{property.Name} = {elementVar}.GetString();"); break; case "int": sb.AppendLine($" config.{property.Name} = {elementVar}.GetInt32();"); break; case "long": sb.AppendLine($" config.{property.Name} = {elementVar}.GetInt64();"); break; case "bool": sb.AppendLine($" config.{property.Name} = {elementVar}.GetBoolean();"); break; case "double": sb.AppendLine($" config.{property.Name} = {elementVar}.GetDouble();"); break; case "float": sb.AppendLine($" config.{property.Name} = {elementVar}.GetSingle();"); break; case "decimal": sb.AppendLine($" config.{property.Name} = {elementVar}.GetDecimal();"); break; case "System.DateTime": case "DateTime": sb.AppendLine($" config.{property.Name} = {elementVar}.GetDateTime();"); break; case "System.Guid": case "Guid": sb.AppendLine($" config.{property.Name} = {elementVar}.GetGuid();"); break; default: // 对于复杂类型和数组,使用JsonSerializer sb.AppendLine($" config.{property.Name} = JsonSerializer.Deserialize<{property.Type}>({elementVar}.GetRawText());"); break; } } private static string FormatDefaultValue(object defaultValue, string type) { if (defaultValue == null) return "null"; if (type == "string") return $"\"{defaultValue}\""; if (type == "bool" || type == "bool?") return defaultValue.ToString()!.ToLower(); if (type == "char") return $"'{defaultValue}'"; return defaultValue.ToString()!; } } }

项目配置文件,很重要。

XML
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <LangVersion>latest</LangVersion> <Nullable>enable</Nullable> <IncludeBuildOutput>false</IncludeBuildOutput> <GeneratePackageOnBuild>false</GeneratePackageOnBuild> <IsRoslynComponent>true</IsRoslynComponent> <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> <PackageId>ConfigurationLib.SourceGenerators</PackageId> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" PrivateAssets="all" /> </ItemGroup> </Project>

ConfigurationLib

C#
using System; using System.Collections.Generic; using System.Text; namespace ConfigurationLib.Attributes { [AttributeUsage(AttributeTargets.Property)] public class ConfigPropertyAttribute : Attribute { public string? Key { get; set; } public object? DefaultValue { get; set; } public bool Required { get; set; } = false; } }
C#
using System; using System.Collections.Generic; using System.Text; namespace ConfigurationLib.Attributes { [AttributeUsage(AttributeTargets.Class)] public class ConfigurationAttribute : Attribute { public string? FileName { get; set; } public string? Section { get; set; } public bool Required { get; set; } = true; } }
C#
using System; using System.Collections.Generic; using System.Text; namespace ConfigurationLib { public interface IConfigurationManager { void LoadConfigurations(string configDirectory = "config"); T GetConfiguration<T>() where T : class, new(); IEnumerable<object> GetAllConfigurations(); } }
XML
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> <LangVersion>latest</LangVersion> </PropertyGroup> </Project>

示例调用

C#
using AppTest.Configurations; namespace AppTest { internal class Program { static void Main(string[] args) { Console.OutputEncoding = System.Text.Encoding.UTF8; Console.WriteLine("=== Configuration Management Demo ==="); Console.WriteLine(); try { // 创建配置管理器实例 var configManager = new ConfigurationLib.Generated.GeneratedConfigurationManager(); // 加载所有配置文件 configManager.LoadConfigurations("config"); Console.WriteLine("✅ Configurations loaded successfully!"); Console.WriteLine(); // 使用数据库配置 DemonstrateUsage(configManager); } catch (Exception ex) { Console.WriteLine($"❌ Error loading configurations: {ex.Message}"); Console.WriteLine($"Stack trace: {ex.StackTrace}"); } Console.WriteLine(); Console.WriteLine("Press any key to exit..."); Console.ReadKey(); } private static void DemonstrateUsage(ConfigurationLib.Generated.GeneratedConfigurationManager configManager) { // 获取数据库配置 var dbConfig = configManager.GetConfiguration<DatabaseConfig>(); if (dbConfig != null) { Console.WriteLine("📊 Database Configuration:"); Console.WriteLine($" Connection String: {MaskConnectionString(dbConfig.ConnectionString)}"); Console.WriteLine($" Command Timeout: {dbConfig.CommandTimeout}s"); Console.WriteLine($" Enable Retry: {dbConfig.EnableRetry}"); Console.WriteLine($" Max Retry Count: {dbConfig.MaxRetryCount}"); Console.WriteLine(); // 模拟数据库连接 ConnectToDatabase(dbConfig); } // 获取API配置 var apiConfig = configManager.GetConfiguration<ApiConfig>(); if (apiConfig != null) { Console.WriteLine("🌐 API Configuration:"); Console.WriteLine($" Base URL: {apiConfig.BaseUrl}"); Console.WriteLine($" API Key: {MaskApiKey(apiConfig.ApiKey)}"); Console.WriteLine($" Timeout: {apiConfig.TimeoutMs}ms"); Console.WriteLine($" Endpoints: [{string.Join(", ", apiConfig.Endpoints ?? new string[0])}]"); Console.WriteLine(); // 模拟API调用 CallApi(apiConfig); } // 获取邮箱 var emailConfig = configManager.GetConfiguration<EmailConfig>(); if (emailConfig != null) { Console.WriteLine("📊 Email Configuration:"); Console.WriteLine($" Host String: {emailConfig.Host}"); Console.WriteLine($" Port : {emailConfig.Port}s"); Console.WriteLine($" EnableSsl : {emailConfig.EnableSsl}s"); Console.WriteLine(); } // 获取日志配置 var loggingConfig = configManager.GetConfiguration<LoggingConfig>(); if (loggingConfig != null) { Console.WriteLine("📝 Logging Configuration:"); Console.WriteLine($" Log Level: {loggingConfig.LogLevel}"); Console.WriteLine($" File Logging: {loggingConfig.EnableFileLogging}"); Console.WriteLine($" Log Path: {loggingConfig.LogPath}"); Console.WriteLine($" Max File Size: {loggingConfig.MaxFileSizeMB}MB"); Console.WriteLine(); // 配置日志系统 ConfigureLogging(loggingConfig); } // 获取功能配置 var featureConfig = configManager.GetConfiguration<FeatureConfig>(); if (featureConfig != null) { Console.WriteLine("🚩 Feature Configuration:"); Console.WriteLine($" Experimental Features: {featureConfig.EnableExperimentalFeatures}"); if (featureConfig.Flags != null) { Console.WriteLine($" New Feature: {featureConfig.Flags.EnableNewFeature}"); Console.WriteLine($" Beta Features: {featureConfig.Flags.EnableBetaFeatures}"); Console.WriteLine($" Feature Expiry: {featureConfig.Flags.FeatureExpiryDate?.ToString("yyyy-MM-dd") ?? "Not set"}"); } if (featureConfig.ExperimentalFeaturesList != null) { Console.WriteLine($" Experimental List: [{string.Join(", ", featureConfig.ExperimentalFeaturesList)}]"); } Console.WriteLine(); // 处理功能标志 HandleFeatureFlags(featureConfig); } // 显示所有加载的配置 Console.WriteLine("📋 All Loaded Configurations:"); var allConfigs = configManager.GetAllConfigurations(); foreach (var config in allConfigs) { Console.WriteLine($" - {config.GetType().Name}"); } } private static void ConnectToDatabase(DatabaseConfig config) { Console.WriteLine("🔌 Simulating database connection..."); // 这里是模拟代码,实际应用中会创建真实的数据库连接 if (!string.IsNullOrEmpty(config.ConnectionString)) { Console.WriteLine(" ✅ Database connected successfully!"); Console.WriteLine($" ⏱️ Using timeout: {config.CommandTimeout}s"); if (config.EnableRetry) { Console.WriteLine($" 🔄 Retry enabled with max count: {config.MaxRetryCount}"); } } else { Console.WriteLine(" ❌ Invalid connection string!"); } Console.WriteLine(); } private static void CallApi(ApiConfig config) { Console.WriteLine("🌍 Simulating API calls..."); if (!string.IsNullOrEmpty(config.BaseUrl)) { Console.WriteLine($" 📡 Connecting to: {config.BaseUrl}"); Console.WriteLine($" ⏱️ Request timeout: {config.TimeoutMs}ms"); if (config.Endpoints != null) { foreach (var endpoint in config.Endpoints) { Console.WriteLine($" 📝 Available endpoint: {config.BaseUrl}{endpoint}"); } } Console.WriteLine(" ✅ API connection established!"); } else { Console.WriteLine(" ❌ Invalid API base URL!"); } Console.WriteLine(); } private static void ConfigureLogging(LoggingConfig config) { Console.WriteLine("📋 Configuring logging system..."); Console.WriteLine($" 📊 Log level set to: {config.LogLevel}"); if (config.EnableFileLogging) { Console.WriteLine($" 📄 File logging enabled: {config.LogPath}"); Console.WriteLine($" 💾 Max file size: {config.MaxFileSizeMB}MB"); // 确保日志目录存在 var logDir = Path.GetDirectoryName(config.LogPath); if (!string.IsNullOrEmpty(logDir) && !Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); Console.WriteLine($" 📁 Created log directory: {logDir}"); } } else { Console.WriteLine(" 📺 Console logging only"); } Console.WriteLine(" ✅ Logging configured successfully!"); Console.WriteLine(); } private static void HandleFeatureFlags(FeatureConfig config) { Console.WriteLine("🎛️ Processing feature flags..."); if (config.Flags != null) { if (config.Flags.EnableNewFeature) { Console.WriteLine(" 🆕 New feature is ENABLED"); } if (config.Flags.EnableBetaFeatures) { Console.WriteLine(" 🧪 Beta features are ENABLED"); } else { Console.WriteLine(" 🚫 Beta features are DISABLED"); } if (config.Flags.FeatureExpiryDate.HasValue && config.Flags.FeatureExpiryDate.Value < DateTime.Now) { Console.WriteLine(" ⚠️ Some features have expired!"); } } if (config.EnableExperimentalFeatures && config.ExperimentalFeaturesList != null) { Console.WriteLine(" 🧬 Experimental features enabled:"); foreach (var feature in config.ExperimentalFeaturesList) { Console.WriteLine($" - {feature}"); } } Console.WriteLine(" ✅ Feature flags processed!"); Console.WriteLine(); } private static string MaskConnectionString(string connectionString) { if (string.IsNullOrEmpty(connectionString)) return "[Not Set]"; return connectionString; } private static string MaskApiKey(string? apiKey) { if (string.IsNullOrEmpty(apiKey)) return "[Not Set]"; return apiKey.Length > 8 ? apiKey.Substring(0, 4) + "****" + apiKey.Substring(apiKey.Length - 4) : "****"; } } }

image.png

对应配置文件

JSON
{ "baseUrl": "https://api.example.com", "apiKey": "your-api-key-here", "timeout": 10000, "endpoints": [ "/users", "/products", "/orders" ] }
JSON
{ "Database": { "connectionString": "Server=localhost;Database=MyApp;Integrated Security=true;", "timeout": 60, "enableRetry": true, "maxRetryCount": 5 } }
JSON
{ "flags": { "EnableNewFeature": true, "EnableBetaFeatures": false, "FeatureExpiryDate": "2024-12-31T23:59:59Z" }, "enableExperimentalFeatures": true, "experimentalFeaturesList": [ "NewUI", "EnhancedSearch", "AIIntegration" ] }
JSON
{ "Logging": { "level": "Debug", "enableFileLogging": true, "logPath": "logs/myapp.log", "maxFileSizeMB": 50 } }

⚠️ 开发中的坑点提醒

  1. 命名空间问题:生成的代码要确保正确引用原始配置类的命名空间
  2. 类型处理:可空类型需要特别处理,避免运行时异常
  3. JSON格式验证:建议添加JSON格式验证,提供友好的错误信息
  4. 性能优化:使用JsonDocument而非JObject,性能更佳

💡 进阶优化技巧

🔥 支持复杂类型

C#
// 自动支持数组、对象等复杂类型 [ConfigProperty(Key = "features")] public FeatureFlags? Flags { get; set; } [ConfigProperty(Key = "endpoints")] public string[]? Endpoints { get; set; }

🎛️ 环境变量支持

可以扩展生成器支持环境变量覆盖:

C#
// 优先级:环境变量 > 配置文件 > 默认值 var envValue = Environment.GetEnvironmentVariable($"APP_{property.ConfigKey.ToUpper()}"); if (!string.IsNullOrEmpty(envValue)) { config.Property = ParseValue(envValue, property.Type); }

🎉 总结:拥抱编译时编程

通过源代码生成器,我们实现了:

  1. 零运行时开销:所有代码在编译时生成,运行时性能最优
  2. 类型安全:编译时就能发现配置错误
  3. 开发效率提升300%:告别重复的模板代码

这个配置管理神器不仅让代码更简洁,还大大提升了开发体验。你在项目中是否也遇到过类似的配置管理痛点?欢迎在评论区分享你的经验,或者说说你希望看到哪些功能扩展!

如果这篇文章对你有帮助,请点赞并分享给更多的.NET开发同伴,让我们一起用更优雅的方式写代码!


想了解更多C#高级技巧?关注我,带你探索更多编程黑科技!

相关信息

通过网盘分享的文件:AppGeneratorsConfig.zip 链接: https://pan.baidu.com/s/1xYO28L100bEjpHPwz6W92A?pwd=jb4m 提取码: jb4m --来自百度网盘超级会员v9的分享

本文作者:技术老小子

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!