C#替代Enum方案咨询:如何实现类Enum的智能感知选值
嘿,我懂你的需求——不想用枚举,但又想要编码时的智能感知选值,还能和数据库里的Countries列联动做验证,对吧?刚好有几种靠谱的实现方式,我给你逐个拆解:
你可以创建一个静态类,把每个国家定义成只读常量,值对应数据库里的存储内容(比如字符串"India")。这样写代码时就能通过智能感知点出所有国家,直接用来做验证:
public static class Countries { public const string India = "India"; public const string China = "China"; public const string UnitedStates = "United States"; // 按需添加其他国家... }
验证时的写法很直观:
// 假设dbCountry是从数据库读取的国家值 if (dbCountry == Countries.India) { // 你的验证逻辑 }
小贴士:这个方案唯一的小缺点是,如果数据库里的国家列表更新了,你得手动修改这个静态类,没法自动同步。适合国家列表变动不频繁的场景。
如果数据库里的国家列表经常变动,手动维护静态类太麻烦,你可以用代码生成工具(比如T4模板、Roslyn源码生成器),自动从数据库读取Countries的所有值,生成包含所有国家属性的静态类。这样每次数据库更新,只要重新生成代码就行,既保留智能感知,又能和数据库同步。
举个简单的T4模板示例(核心是从数据库查询后生成代码):
<#@ template language="C#" #> <#@ assembly name="System.Data" #> <#@ import namespace="System.Data.SqlClient" #> public static class Countries { <# // 替换成你的数据库连接字符串 var connectionString = "Server=your_server;Database=your_db;Trusted_Connection=True;"; using(var conn = new SqlConnection(connectionString)) { conn.Open(); var cmd = new SqlCommand("SELECT DISTINCT CountryName FROM Countries", conn); var reader = cmd.ExecuteReader(); while(reader.Read()) { var countryName = reader["CountryName"].ToString(); // 处理带空格的国家名,比如把"United States"转成"United_States"作为属性名 var propertyName = countryName.Replace(" ", "_").Replace("-", "_"); #> public const string <#= propertyName #> = "<#= countryName #>"; <# } } #> }
生成后的类和方案1结构一致,但完全不用手动维护,省心很多。
如果想要应用启动时自动同步数据库数据,不用每次改数据库都重新生成代码,可以用单例类配合静态构造函数,在应用启动时加载所有国家,然后提供布尔属性来判断:
public static class CountryChecker { public static bool IsIndia { get; private set; } public static bool IsChina { get; private set; } // 其他国家的IsXXX属性... static CountryChecker() { // 从数据库加载所有国家到集合 var allDbCountries = LoadCountriesFromDatabase(); IsIndia = allDbCountries.Contains("India"); IsChina = allDbCountries.Contains("China"); // 其他国家的判断逻辑... } private static HashSet<string> LoadCountriesFromDatabase() { var countries = new HashSet<string>(); // 替换成你的连接字符串 using(var conn = new SqlConnection("your_connection_string")) { conn.Open(); var cmd = new SqlCommand("SELECT DISTINCT CountryName FROM Countries", conn); var reader = cmd.ExecuteReader(); while(reader.Read()) { countries.Add(reader["CountryName"].ToString()?.Trim() ?? string.Empty); } } return countries; } }
然后验证时可以直接这样写:
if (CountryChecker.IsIndia) { // 执行印度相关的验证逻辑 }
注意:这个方案是在应用启动时加载一次数据,如果数据库在运行中更新了,需要重启应用或者手动添加缓存刷新逻辑。
如果每个国家需要不同的验证规则,用记录类型封装会更优雅——把国家的标识和对应的验证逻辑绑在一起,同时保留智能感知:
public record Country(string Name) { // 预定义所有国家实例 public static readonly Country India = new("India"); public static readonly Country China = new("China"); public static readonly Country UnitedStates = new("United States"); // 每个国家的专属验证方法 public bool Validate() { return Name switch { "India" => ValidateIndiaSpecificRules(), "China" => ValidateChinaSpecificRules(), "United States" => ValidateUsSpecificRules(), _ => false }; } private bool ValidateIndiaSpecificRules() { // 印度的专属验证逻辑 return true; } private bool ValidateChinaSpecificRules() { // 中国的专属验证逻辑 return true; } private bool ValidateUsSpecificRules() { // 美国的专属验证逻辑 return true; } }
使用时的写法也很清晰:
// 假设从数据库获取的国家名称是dbCountryName var targetCountry = Country.India; if (targetCountry.Name == dbCountryName) { bool isValid = targetCountry.Validate(); // 处理验证结果 }
这种方式更符合面向对象的设计,适合每个国家有独特验证规则的场景。
内容的提问来源于stack exchange,提问作者rajeshnrh

