1.定义一个存储枚举的显示名称的定制特性;2.为每个枚举的成员添加此定制特性;3.写显示枚举名称、绑定控件的扩展方法;
1.定义一个存储枚举的显示名称的定制特性:

class EnumShowNameAttribute : Attribute
/// <summary>
/// 枚举的显示名称
/// </summary>
[global::System.AttributeUsage(AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
public sealed class EnumShowNameAttribute : Attribute
{
/// <summary>
/// 显示名称
/// </summary>
public string ShowName { get; private set; }
/// <summary>
/// 构造枚举的显示名称
/// </summary>
/// <param name="showName">显示名称</param>
public EnumShowNameAttribute(string showName)
{
this.ShowName = showName;
}
}
2.为每个枚举的成员添加此定制特性。如:

Code
/// <summary>
/// 性别
/// </summary>
public enum Sex
{
/// <summary>
/// 男
/// </summary>
[EnumShowName("男")]
Man=0,
/// <summary>
/// 女
/// </summary>
[EnumShowName("女")]
Woman=1,
}
3.写显示枚举名称、绑定控件的扩展方法。
显示枚举名称方法(ToShowName())的思路本来很简单:根据枚举值获取其EnumShowNameAttribute定制特性的ShowName属性值。但我这里的代码并不少,主要用来处理以下几点:
1.缓存。由于用反射的方式获取值性能不好。于是这里用了一个Dictionary<string,string>来缓存枚举值对应的ShowName,Key是[枚举类的FullName].[枚举值的ToString],Value是ShowName值。那么只是第一次访问的时候需要反射,以后都从缓存里取就行了。
2.FlagsAttribute标记的枚举。此标志表示这个枚举按位排列。那么不缓存它,且需要拿输入值与枚举的成员一个一个比较。
另:绑定的时候应尊重控件的AppendDataBoundItems属性,给使用时更多选择。

Code
static private Dictionary<string, string> _cacheEnumShowNamedDic = new Dictionary<string, string>();


/**//// <summary>
/// 输出枚举的ShowName,需要枚举声明EnumShowNameAttribute
/// </summary>
/// <param name="en">待输出的枚举</param>
/// <returns></returns>
static public string ToShowName(this Enum en)

{
return ToShowName(en, false, ",");
}


/**//// <summary>
/// 输出枚举的ShowName,需要枚举声明EnumShowNameAttribute
/// </summary>
/// <param name="en">待输出的枚举</param>
/// <param name="exceptionIfNotSuccess">未成功是否抛出异常</param>
/// <param name="flagsSeparator">Flags类型输出时的间隔符</param>
/// <returns></returns>
static public string ToShowName(this Enum en, bool exceptionIfFail,string flagsSeparator)

{
string showName;
string enFullName = GetEnumFullName(en);

// 先在缓存中找
if (!_cacheEnumShowNamedDic.TryGetValue(enFullName, out showName))

{
// 获取枚举类型
Type enumType = en.GetType();

// 查看其是否Flags
object[] flagsAtts = enumType.GetCustomAttributes(typeof(FlagsAttribute), false);

// 有Flags,按位处理,不缓存
if (flagsAtts != null && flagsAtts.Length > 0)

{
long currentVal = Convert.ToInt64(en);
StringBuilder sb = new StringBuilder();

string[] names = Enum.GetNames(enumType);
string zeroString = "";

foreach (string name in names)

{
long val = Convert.ToInt64(Enum.Parse(enumType, name));

// val为0,则跳过
if (val == 0)

{
object[] atts = enumType.GetField(name).GetCustomAttributes(typeof(EnumShowNameAttribute), false);
if (atts.Length > 0)

{
// 找到,设置
zeroString = ((EnumShowNameAttribute)atts[0]).ShowName;
}
continue;
}

// 比较
if ((val & currentVal) == val)

{
// 加间隔符
if (sb.Length != 0)

{
sb.Append(flagsSeparator);
}

// 加上定制特性值
object[] atts = enumType.GetField(name).GetCustomAttributes(typeof(EnumShowNameAttribute), false);
if (atts.Length > 0)

{
// 找到,设置
sb.Append(((EnumShowNameAttribute)atts[0]).ShowName);
}
else

{
// 没找到定制特性,参数指定抛出异常则抛出异常
if (exceptionIfFail)

{
throw new InvalidOperationException(string.Format("此枚举{0}未定义EnumShowNameAttribute", enFullName));
}

sb.Append(name);
}
}
}

// 输出
if (sb.Length > 0)

{
return sb.ToString();
}
return zeroString;
}
// 无Flags
else

{
// 找字段
FieldInfo fieldInfo = enumType.GetField(en.ToString());
if (fieldInfo == null)

{
throw new InvalidOperationException(string.Format("非完整枚举{0}", enFullName));
}

// 找定制特性
object[] atts = fieldInfo.GetCustomAttributes(typeof(EnumShowNameAttribute), false);
if (atts.Length > 0)

{
// 找到,设置
showName = ((EnumShowNameAttribute)atts[0]).ShowName;
lock (_cacheEnumShowNamedDic)

{
_cacheEnumShowNamedDic[enFullName] = showName;
}
}
else

{
// 没找到定制特性,参数指定抛出异常则抛出异常
if (exceptionIfFail)

{
throw new InvalidOperationException(string.Format("此枚举{0}未定义EnumShowNameAttribute", enFullName));
}

// 否则直接输出本身的ToString
return en.ToString();
}
}
}

return showName;
}


/**//// <summary>
/// 获取枚举的全名
/// </summary>
/// <param name="en"></param>
/// <returns></returns>
static private string GetEnumFullName(Enum en)

{
return en.GetType().FullName + "." + en.ToString();
}


/**//// <summary>
/// 给ListControl绑定枚举值
/// </summary>
/// <typeparam name="T">枚举类型</typeparam>
/// <param name="listControl">待绑定的控件</param>
static public void BindEnum<T>(this System.Web.UI.WebControls.ListControl listControl)
where T : struct

{
// 获取类型
Type type = typeof(T);
if (!type.IsEnum)

{
throw new InvalidOperationException("类型必须是枚举:" + type.FullName);
}

// 获取枚举成员
Array values = Enum.GetValues(type);

// 绑定值
if (!listControl.AppendDataBoundItems)

{
listControl.Items.Clear();
}
foreach (Enum item in values)

{
listControl.Items.Add(new System.Web.UI.WebControls.ListItem(item.ToShowName(), item.ToString()));
}
}

4.使用。直接在枚举上ToShowName就可以得到显示的名称,在ListControl(DropDownList继承此类)用BindEnum方法绑定枚举。如:
this.ddlTest.BindEnum<Sex>();
string str = Sex.Man.ToShowName();
注:有多语言需求的项目和大项目,不推荐使用这种方案,而建议将显示的文字存储在单独的文件中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述