我有一个包含以下内容的枚举(例如):
- UnitedKingdom,
- UnitedStates,
- France,
- Portugal
在我的代码中,我使用 Country.UnitedKingdom ,但如果我将其分配给 string ,我希望该值为 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();
正如评论中所提到的,使用默认字典比较器会涉及装箱,这是不理想的。对于一次性的情况,我可以接受,直到发现它成为瓶颈。如果我要为多个枚举编写此代码,我会编写一个可重用的类。
Switch语句
我同意yshuditelu的答案建议对于相对较少的情况使用switch
语句。然而,由于每个case将是单个语句,我个人会为此情况改变我的编码风格,以保持代码紧凑但易读:
public static string ToBriefName(this Country country)
{
switch (country)
{
case Country.UnitedKingdom: return "UK";
case Country.UnitedStates: return "US";
default: return country.ToString();
}
}
您可以添加更多的情况,而不会让它变得太庞大,而且很容易从枚举值转到返回值。
DescriptionAttribute
关于DescriptionAttribute
代码可重用性的点Rado made是一个很好的点,但在这种情况下,我建议不要每次需要获取值时都使用反射。我可能会编写一个通用的静态类来保存查找表(可能是一个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();
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'.
的错误信息。初步研究表明,它不喜欢枚举不是一个实例? - Alex AngasGetFields
调用添加一些绑定标志,以排除具有特殊名称value__
(参见.Net 4.6.1中的enumbuilder.cs的第416行)的非静态、私有后备字段。例如,此行有效:FieldInfo[] fieldInfos = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static);
- Tyson Williams你可以创建一个扩展方法public static string ToShortString(this Country country)
。在这个方法中,你可以像Jon建议的那样使用静态字典,或者你可以使用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 const string UnitedKingdom = "UK";
public const string France = "F";
}
还有一种可能性没有被提到,就是这样的:
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; }
}
从这个点开始,您可以添加更多的属性,但要注意不要向类中添加太多内容或静态成员,因为它会增加内存负担,可能不值得。
我认为很少有情况下这种策略是最佳方法,但当您想要将某些东西视为基本枚举时,这是一种需要注意的选项,可以尝试添加属性或属性。
我试图提交对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。没有理由按字母顺序排列说明。实际上,排序会破坏枚举的原始顺序。字典不保留枚举顺序,但至少不像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;
}
如果您仅需要获取枚举值的字符串表示形式,则无需创建字典。请参阅此示例
[编辑]哦...忘记提到它比字典更可重用,因为您只需要一个常见的util类来帮助获取描述,然后您所需要做的就是在添加枚举值或创建具有相同要求的新枚举时添加DescriptionAttribute。在字典/ switch解决方案中,它更难以维护,并且一旦您拥有许多枚举类型,它会变得混乱。
var codes = new Dictionary<Country, string>()
{ { Country.UnitedKingdom, "UK" },
{ Country.UnitedStates, "US" },
{ Country.France, "FR" } };
Console.WriteLine(codes[Country.UnitedStates]);