You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#枚举匹配是否有更优雅高性能的非遍历实现方案?

C# 多公司枚举映射优雅实现方案

你现在代码冗余的核心原因是把员工-公司、公司-行为值的绑定逻辑硬编码在了分支判断里,每加一家公司就要复制粘贴一段遍历代码,维护成本随公司/员工数量线性上涨。下面两个方案都完全离线运行,不需要数据库或外部依赖,扩展成本极低。


方案1:反射自动扫描映射(零硬编码,扩展最方便)

核心逻辑是给相关枚举加统一标记,程序启动时自动扫描所有枚举构建内存查找表,后续查询直接走字典O(1)返回结果,新增公司/员工时不需要修改核心查找逻辑。

  • 首先定义两个标记特性,用来区分员工枚举和行为枚举:
    [AttributeUsage(AttributeTargets.Enum)]
    internal class CompanyEmployeeEnumAttribute : Attribute { }
    
    [AttributeUsage(AttributeTargets.Enum)]
    internal class CompanyActionEnumAttribute : Attribute { }
    
  • 给原有枚举加上对应标记:
    [CompanyEmployeeEnum]
    internal enum CompanyA
    {
        workerA = 21,
        workerB = 42,
    }
    
    [CompanyEmployeeEnum]
    internal enum CompanyB
    {
        workerC = 22,
        workerD = 44,
    }
    
    [CompanyActionEnum]
    internal enum CompanyA_Do
    {
        DoA = 11,
        DoB = 5,
    }
    
    [CompanyActionEnum]
    internal enum CompanyB_Do
    {
        DoA = 15,
        DoB = 55,
    }
    
  • 写静态解析类,程序启动时一次性加载所有映射关系到内存:
    internal static class CompanyConfigResolver
    {
        // 员工名 -> 所属公司枚举类型 映射
        private static readonly IReadOnlyDictionary<string, Type> _employeeToCompanyMap;
        // 公司枚举类型 -> (行为名, 行为值) 映射
        private static readonly IReadOnlyDictionary<Type, IReadOnlyDictionary<string, int>> _companyActionMap;
    
        static CompanyConfigResolver()
        {
            // 扫描当前程序集所有标记过的枚举
            var allMarkedEnums = AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(asm => asm.GetTypes())
                .Where(t => t.IsEnum)
                .Where(t => t.GetCustomAttributes(inherit: false)
                    .Any(attr => attr is CompanyEmployeeEnumAttribute or CompanyActionEnumAttribute))
                .ToList();
    
            var employeeEnums = allMarkedEnums.Where(t => t.GetCustomAttributes<CompanyEmployeeEnumAttribute>(false).Any()).ToList();
            var actionEnums = allMarkedEnums.Where(t => t.GetCustomAttributes<CompanyActionEnumAttribute>(false).Any()).ToList();
    
            // 构建员工所属公司映射
            var empMap = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
            foreach (var empEnumType in employeeEnums)
            {
                foreach (var empName in Enum.GetNames(empEnumType))
                {
                    empMap[empName] = empEnumType;
                }
            }
            _employeeToCompanyMap = empMap;
    
            // 构建公司行为值映射
            var actionMap = new Dictionary<Type, IReadOnlyDictionary<string, int>>();
            foreach (var actionEnum in actionEnums)
            {
                // 行为枚举名去掉末尾的"_Do"后缀,就是对应员工枚举的类名
                string matchedCompanyEnumName = actionEnum.Name[..^3];
                var matchedCompanyEnum = employeeEnums.First(t => t.Name == matchedCompanyEnumName);
                
                var actionValueMap = Enum.GetValues(actionEnum)
                    .Cast<object>()
                    .ToDictionary(
                        keySelector: val => Enum.GetName(actionEnum, val)!,
                        elementSelector: val => (int)val
                    );
                actionMap[matchedCompanyEnum] = actionValueMap;
            }
            _companyActionMap = actionMap;
        }
    
        /// <summary>
        /// 获取当前登录用户对应的行为配置值
        /// </summary>
        public static bool TryGetUserActionValue(bool isDoA, bool isDoB, out int workerDoValue, out bool isCompanyA)
        {
            workerDoValue = default;
            isCompanyA = default;
            string currentUser = Environment.UserName;
    
            // 未匹配到员工直接返回失败
            if (!_employeeToCompanyMap.TryGetValue(currentUser, out var companyType))
                return false;
    
            isCompanyA = companyType == typeof(CompanyA);
            string targetActionName = isDoA ? "DoA" : isDoB ? "DoB" : string.Empty;
            if (string.IsNullOrEmpty(targetActionName))
                return false;
    
            return _companyActionMap[companyType].TryGetValue(targetActionName, out workerDoValue);
        }
    }
    
  • 调用方式非常简单,不需要写任何遍历或分支判断公司:
    if (CompanyConfigResolver.TryGetUserActionValue(isDoA, isDoB, out int doValue, out bool isCompanyA))
    {
        // doValue就是需要的CompanyX_Do对应int值,isCompanyA是所属公司标记
        // 直接使用即可
    }
    else
    {
        // 处理当前用户不在员工列表的异常逻辑
    }
    

后续新增公司时,只需要新增CompanyX员工枚举和CompanyX_Do行为枚举,加上对应标记即可,不需要修改任何解析逻辑。


方案2:集中字典映射(无反射,更轻量)

如果不想引入反射,可以直接把所有映射关系集中在一个静态字典里初始化,避免散落在多个遍历分支中:

internal static class SimpleCompanyConfig
{
    // 集中存储所有员工对应的行为值,同公司员工配置相同可以直接复用
    private static readonly IReadOnlyDictionary<string, (int DoA, int DoB)> _userConfig = 
        new Dictionary<string, (int, int)>()
        {
            { nameof(CompanyA.workerA), ((int)CompanyA_Do.DoA, (int)CompanyA_Do.DoB) },
            { nameof(CompanyA.workerB), ((int)CompanyA_Do.DoA, (int)CompanyA_Do.DoB) },
            { nameof(CompanyB.workerC), ((int)CompanyB_Do.DoA, (int)CompanyB_Do.DoB) },
            { nameof(CompanyB.workerD), ((int)CompanyB_Do.DoA, (int)CompanyB_Do.DoB) },
        };

    public static bool TryGetCurrentUserValue(bool isDoA, bool isDoB, out int workerDoValue, out bool isCompanyA)
    {
        workerDoValue = default;
        isCompanyA = default;
        string currentUser = Environment.UserName;
        
        if (!_userConfig.TryGetValue(currentUser, out var config))
            return false;

        isCompanyA = currentUser is nameof(CompanyA.workerA) or nameof(CompanyA.workerB);
        workerDoValue = isDoA ? config.DoA : isDoB ? config.DoB : default;
        return workerDoValue != default;
    }
}

这个方案性能更高,新增员工/公司时只需要在字典里加一行配置即可,维护成本远低于原来的多段遍历写法。


方案对比

  • 方案1适合公司/员工规模较大、后续扩展频繁的场景,完全不需要手动维护映射关系,新增枚举即可自动加载
  • 方案2适合规模较小、不想用反射的场景,代码更直白,调试方便
  • 两个方案都是启动时一次性加载配置,运行时查询性能都是O(1),完全离线,不需要任何外部存储支持

内容的提问来源于stack exchange,提问作者robni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 04:55:19