[英]C# Getting Enum values
我有一个包含以下内容的枚举(例如):
在我的代码中,我使用Country.UnitedKingdom ,但如果我将其分配给一个字符串,我希望该值为UK 。
这可能吗?
您不能将枚举值分配给字符串作为开头。 您必须调用ToString()
,这会将Country.UnitedKingdom
转换为“UnitedKingdom”。
有两种选择:
Dictionary<Country, string>
关于他们每个人的评论......
Dictionary<Country,string>
的示例代码
using System;
using System.Collections.Generic;
enum Country
{
UnitedKingdom,
UnitedStates,
France,
Portugal
}
class Test
{
static readonly Dictionary<Country, string> CountryNames =
new Dictionary<Country, string>
{
{ Country.UnitedKingdom, "UK" },
{ Country.UnitedStates, "US" },
};
static string ConvertCountry(Country country)
{
string name;
return (CountryNames.TryGetValue(country, out name))
? name : country.ToString();
}
static void Main()
{
Console.WriteLine(ConvertCountry(Country.UnitedKingdom));
Console.WriteLine(ConvertCountry(Country.UnitedStates));
Console.WriteLine(ConvertCountry(Country.France));
}
}
您可能希望将ConvertCountry
的逻辑放入扩展方法中。 例如:
// Put this in a non-nested static class
public static string ToBriefName(this Country country)
{
string name;
return (CountryNames.TryGetValue(country, out name))
? name : country.ToString();
}
然后你可以写:
string x = Country.UnitedKingdom.ToBriefName();
如评论中所述,默认字典比较器将涉及装箱,这是不理想的。 对于一次性的,我会接受它,直到我发现它是一个瓶颈。 如果我对多个枚举执行此操作,我会编写一个可重复使用的 class。
开关语句
我同意yshuditelu 的回答,建议在相对较少的情况下使用switch
语句。 然而,由于每个案例都将是一个单一的陈述,我个人会针对这种情况改变我的编码风格,以保持代码紧凑但可读:
public static string ToBriefName(this Country country)
{
switch (country)
{
case Country.UnitedKingdom: return "UK";
case Country.UnitedStates: return "US";
default: return country.ToString();
}
}
您可以向其添加更多案例,而不会变得太大,并且很容易将您的目光从枚举值转移到返回值。
DescriptionAttribute
Rado关于DescriptionAttribute
代码可重用的观点很好,但在那种情况下,我建议不要在每次需要获取值时都使用反射。 我可能会写一个通用的 static class 来保存一个查找表(可能是一个Dictionary
,可能带有评论中提到的自定义比较器)。 扩展方法不能在泛型类中定义,所以你可能会得到类似这样的结果:
public static class EnumExtensions
{
public static string ToDescription<T>(this T value) where T : struct
{
return DescriptionLookup<T>.GetDescription(value);
}
private static class DescriptionLookup<T> where T : struct
{
static readonly Dictionary<T, string> Descriptions;
static DescriptionLookup()
{
// Initialize Descriptions here, and probably check
// that T is an enum
}
internal static string GetDescription(T value)
{
string description;
return Descriptions.TryGetValue(value, out description)
? description : value.ToString();
}
}
}
我更喜欢在我的枚举上使用DescriptionAttribute 。 然后,您可以使用以下代码从枚举中获取该描述。
enum MyCountryEnum
{
[Description("UK")]
UnitedKingdom = 0,
[Description("US")]
UnitedStates = 1,
[Description("FR")]
France = 2,
[Description("PO")]
Portugal = 3
}
public static string GetDescription(this Enum value)
{
var type = value.GetType();
var fi = type.GetField(value.ToString());
var descriptions = fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
return descriptions.Length > 0 ? descriptions[0].Description : value.ToString();
}
public static SortedDictionary<string, T> GetBoundEnum<T>() where T : struct, IConvertible
{
// validate.
if (!typeof(T).IsEnum)
{
throw new ArgumentException("T must be an Enum type.");
}
var results = new SortedDictionary<string, T>();
FieldInfo[] fieldInfos = typeof(T).GetFields();
foreach (var fi in fieldInfos)
{
var value = (T)fi.GetValue(fi);
var description = GetDescription((Enum)fi.GetValue(fi));
if (!results.ContainsKey(description))
{
results.Add(description, value);
}
}
return results;
}
然后要获取我的绑定枚举列表,只需调用
GetBoundEnum<MyCountryEnum>()
要获得单个枚举的描述,您只需使用这样的扩展方法
string whatever = MyCountryEnum.UnitedKingdom.GetDescription();
您可以创建一个扩展方法public static string ToShortString(this Country country)
。 在该方法中,您可以按照 Jon 的建议使用 static 字典,或者您可以简单地做一个 switch case。
例子:
public static class CountryExtensions
{
public static string ToShortString( this Country target )
{
switch (target) {
case Country.UnitedKingdom:
return "UK";
case Country.UnitedStates:
return "US";
case Country.France:
return "FR";
case Country.Portugal:
return "PT";
default:
return "None";
}
}
}
伪代码:
enum MyCountryEnum
{
UnitedKingdom = 0,
UnitedStates = 1,
France = 2,
Portugal = 3,
}
string[] shortCodes = new string[] {"UK", "US", "FR", "PO"};
MyCountryEnum enumValue = MyCountryEnum.UnitedKingdom;
string code = shortCodes[enumValue];
另一种没有提到的可能性是这样的:
public class Country
{
public static readonly Country UnitedKingdom = new Country("UK");
public static readonly Country UnitedStates = new Country("US");
public static readonly Country France = new Country("FR");
public static readonly Country Protugal = new Country("PT");
private Country(string shortName)
{
ShortName = shortName;
}
public string ShortName { get; private set; }
}
从这一点开始,您可以添加更多属性,但要注意您向 class 添加了多少,以及您添加了多少 static 成员,因为它添加的 memory 膨胀可能使它不值得。
我不认为在很多情况下这种策略是最好的方法,但在尝试将属性或特性添加到您希望能够将其视为本质上是枚举的对象时,这是一个需要注意的选项。
我不得不暂时离开这个项目的工作,回来后,我有了灵感。
我创建了一个新的 class 而不是枚举,如下所示:
public class Country
{
public const string UnitedKingdom = "UK";
public const string France = "F";
}
这样我就可以在我的代码中使用 Country.UnitedKingdom 并且将使用值“UK”。
我只是将此答案作为替代解决方案发布。
尼尔
如果您只需要为枚举值获取字符串表示形式,则无需创建字典。 看这个例子
[编辑] 哦...忘了提它比字典更可重用,因为您只需要一个通用工具 class 来帮助获取描述,然后您需要做的就是在下次添加枚举值时添加 DescriptionAttribute或者您创建一个具有相同要求的新枚举。 在字典/开关解决方案中,它更难维护,一旦你有很多枚举类型,它就会变得混乱。
我试图提交对 Scott Ivey 的回答的修改,但被拒绝了,这是另一个答案。 我相对较小的编辑:
1) 我修复了 Alex 的System.ArgumentException: Field 'value__' defined on type 'MyClass.EnumHelperTest+MyCountryEnum' is not a field on the target object which is of type 'System.Reflection.RtFieldInfo'.
从这里得到它。
2)添加了一个return
,这样你就可以实际复制/粘贴它并且它会起作用。
3) 将 SortedDictionary 更改为 Dictionary,因为 SortedDictionary 始终按键排序,在本例中为字符串 Description。 没有理由按字母顺序排列描述。 事实上,对其进行排序会破坏枚举的原始顺序。 Dictionary 也不保留枚举顺序,但至少它不像 SortedDictionary 那样暗示顺序。
enum MyCountryEnum
{
[Description("UK")]
UnitedKingdom = 0,
[Description("US")]
UnitedStates = 1,
[Description("FR")]
France = 2,
[Description("PO")]
Portugal = 3
}
public static string GetDescription(this Enum value)
{
var type = value.GetType();
var fi = type.GetField(value.ToString());
var descriptions = fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
return descriptions.Length > 0 ? descriptions[0].Description : value.ToString();
}
public static Dictionary<string, T> GetBoundEnum<T>() where T : struct, IConvertible
{
// validate.
if (!typeof(T).IsEnum)
{
throw new ArgumentException("T must be an Enum type.");
}
var results = new Dictionary<string, T>();
FieldInfo[] fieldInfos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static);
foreach (var fi in fieldInfos)
{
var value = (T)fi.GetValue(fi);
var description = GetDescription((Enum)fi.GetValue(fi));
if (!results.ContainsKey(description))
{
results.Add(description, value);
}
}
return results;
}
每当我看到枚举时,我都觉得应该重构代码。 为什么不创建一个 Country class 并添加方法来完成您试图绕过的一些障碍。 为枚举赋值是一种更大的代码味道。
为什么反对票? 我认为使用多态方法比使用枚举更好这一观点已被广泛接受。 当您可以改用 ValueObject 设计时,使用枚举的理由为零。
这是关于该主题的一篇很好的博客文章: http://devpinoy.org/blogs/cruizer/archive/2007/09/12/enums-are-evil.aspx
var codes = new Dictionary<Country, string>()
{ { Country.UnitedKingdom, "UK" },
{ Country.UnitedStates, "US" },
{ Country.France, "FR" } };
Console.WriteLine(codes[Country.UnitedStates]);
以下解决方案有效(编译并运行)。 我看到两个问题:
您必须确保枚举是同步的。 (自动化测试可以为您做到这一点。)
你会依赖于枚举在 .NET 中不是类型安全的事实。
enum Country { UnitedKingdom = 0, UnitedStates = 1, France = 2, Portugal = 3 } enum CountryCode { UK = 0, US = 1, FR = 2, PT = 3 } void Main() { string countryCode = ((CountryCode)Country.UnitedKingdom).ToString(); Console.WriteLine(countryCode); countryCode = ((CountryCode)Country.Portugal).ToString(); Console.WriteLine(countryCode); }
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.