Ham*_*jan 8 c# reflection enums .net-3.5
这是一个有点简化的例子(我改变它来隐藏实际的代码).我有一个数据库驱动的应用程序和一个单独开发的小工具,可以与应用程序一起使用.有一个表定义了枚举,但它可能会随着时间的推移而发生变化.假设某些app(医疗?)需要相当精确地追踪一个人的性别.
select * from sex order by id;
id | mnemonic | description
0 | U | Unknown sex
1 | M | Male
2 | F | Female
3 | T | Trans-gender
Run Code Online (Sandbox Code Playgroud)
我的C# enum:
public enum SexType
{
/// <summary>Unknown sex.</summary>
[Description("U")]
Unknown = 0,
/// <summary>Male sex.</summary>
[Description("M")]
Male = 1,
/// <summary>Female sex.</summary>
[Description("F")]
Female = 2
/// <summary>Trans-gender sex.</summary>
[Description("T")]
TransGender = 3,
}
Run Code Online (Sandbox Code Playgroud)
在这里,我不应该假设id是一个连续的序列; 这些枚举标志都不能组合,即使它看起来像那样.
一些逻辑是在SQL中完成的; 一些是在C#代码中完成的.例如,我可能有一个功能:
// Here we get id for some record from the database.
public static void IsMaleOrFemale(int sexId)
{
if (!Enum.IsDefined(typeof(SexType), sexId))
{
string message = String.Format("There is no sex type with id {0}.",
sexId);
throw new ArgumentException(message, "sexId");
}
var sexType = (SexType) sexId;
return sexType == SexType.Male || sexType == SexType.Female;
}
Run Code Online (Sandbox Code Playgroud)
只要表和枚举定义都没有改变,这就可以很好地工作.但桌子可能会.我不能依赖id列或保持其值的助记符列.我认为我能做的最好的是进行单元测试,以确保表格与我对枚举的定义同步.我试图将值与id匹配,并将description属性与助记符列匹配.
那么,如何获得所有对的列表(以编程方式,在C#):(0, "U"), (1, "M"), (2, "F"), (3, "T")通过查看enum SexType?
我们的想法是将这个有序列表与select id, mnemonic from sex order by is asc;.
为什么不将SexType从枚举更改为类(或Struct)并在运行时从数据库中填充List?
您的结构(或类)看起来像这样:
public struct SexType
{
public string Type;
public string Code;
public int Value;
}
Run Code Online (Sandbox Code Playgroud)
然后,您可以从数据库中填充a List<SexType>(或者,如果您使用的是EF,则可以下载SexType类型的实体列表,或者您的解决方案允许的任何实体).
假设您正在使用Linq和EF,请在应用程序启动时进行急切加载,并且您应该可以随意使用.
查看有形T4编辑器.
我用它来做到这一点.
安装它,然后将此文件添加到您的项目中(有关此博客文章的更多信息):
EnumGenerator.ttinclude
<#@ template debug="true" hostSpecific="true" #>
<#@ output extension=".generated.cs" #>
<#@ Assembly Name="System.Data" #>
<#@ import namespace="System.Data" #>
<#@ import namespace="System.Data.SqlClient" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#
string tableName = Path.GetFileNameWithoutExtension(Host.TemplateFile);
string path = Path.GetDirectoryName(Host.TemplateFile);
string columnId = "ID";
string columnName = "NAME";
string connectionString = "[your connection string]";
#>
using System;
using System.CodeDom.Compiler;
namespace Your.NameSpace.Enums
{
/// <summary>
/// <#= tableName #> auto generated enumeration
/// </summary>
[GeneratedCode("TextTemplatingFileGenerator", "10")]
public enum <#= tableName #>
{
<#
SqlConnection conn = new SqlConnection(connectionString);
string command = string.Format("select {0}, {1} from {2} order by {0}", columnId, columnName, tableName);
SqlCommand comm = new SqlCommand(command, conn);
conn.Open();
SqlDataReader reader = comm.ExecuteReader();
bool loop = reader.Read();
while(loop)
{
#>
/// <summary>
/// <#= reader[columnName] #> configuration setting.
/// </summary>
<#= reader[columnName] #> = <#= reader[columnId] #><# loop = reader.Read(); #><#= loop ? ",\r\n" : string.Empty #>
<# }
#> }
}
<#+ private string Pascalize(object value)
{
Regex rx = new Regex(@"(?:^|[^a-zA-Z]+)(?<first>[a-zA-Z])(?<reminder>[a-zA-Z0-9]+)");
return rx.Replace(value.ToString(), m => m.Groups["first"].ToString().ToUpper() + m.Groups["reminder"].ToString().ToLower());
}
private string GetSubNamespace()
{
Regex rx = new Regex(@"(?:.+Services\s)");
string path = Path.GetDirectoryName(Host.TemplateFile);
return rx.Replace(path, string.Empty).Replace("\\", ".");
}
#>
Run Code Online (Sandbox Code Playgroud)
(填写类的名称空间和连接字符串)
然后,您只需添加一行空白TT文件,其中包含一行<#@ include file ="EnumGenerator.ttinclude"#>`.此文件的名称应与表的名称相同,并且除非在枚举生成器类中更改,否则此表的列必须命名为"ID"和"NAME".
每当您保存TT文件时,将自动生成枚举.
var choices = Enumerable.Zip(
Enum.GetNames(typeof(SexType)),
Enum.GetValues(typeof(SexType)).Cast<SexType>(),
(name, value) => Tuple.Create(name, value));
Run Code Online (Sandbox Code Playgroud)
如果您只命名枚举值 U、M、F 和 T,会更容易。如果您这样做,类Enum的静态方法会为您完成所有工作。
除此之外,您需要使用一些反射来挖掘描述属性
public IEnumerable<Tuple<string, int>> GetEnumValuePairs(Type enumType)
{
if(!enumType.IsEnum)
{
throw new ArgumentException();
}
List<Tuple<string, int>> result = new List<Tuple<string, int>>();
foreach (var value in Enum.GetValues(enumType))
{
string fieldName = Enum.GetName(enumType, value);
FieldInfo fieldInfo = enumType.GetField(fieldName);
var descAttribute = fieldInfo.GetCustomAttributes(false).Where(a => a is DescriptionAttribute).Cast<DescriptionAttribute>().FirstOrDefault();
// ideally check if descAttribute is null here
result.Add(Tuple.Create(descAttribute.Description, (int)value));
}
return result;
}
Run Code Online (Sandbox Code Playgroud)