我知道以下是不可能的,因为Enumeration的类型必须是int
enum GroupTypes
{
TheGroup = "OEM",
TheOtherGroup = "CMB"
}
从我的数据库中,我得到了一个不完整代码(the OEM
和CMB
s)的字段。我想将此字段变成一个enum
或其他可以理解的字段。因为如果目标是可读性,则解决方案应该简洁。
我还有什么其他选择?
我知道以下是不可能的,因为Enumeration的类型必须是int
enum GroupTypes
{
TheGroup = "OEM",
TheOtherGroup = "CMB"
}
从我的数据库中,我得到了一个不完整代码(the OEM
和CMB
s)的字段。我想将此字段变成一个enum
或其他可以理解的字段。因为如果目标是可读性,则解决方案应该简洁。
我还有什么其他选择?
Answers:
我喜欢在类而不是方法中使用属性,因为它们看起来更像枚举。
这是一个记录器的示例:
public class LogCategory
{
private LogCategory(string value) { Value = value; }
public string Value { get; set; }
public static LogCategory Trace { get { return new LogCategory("Trace"); } }
public static LogCategory Debug { get { return new LogCategory("Debug"); } }
public static LogCategory Info { get { return new LogCategory("Info"); } }
public static LogCategory Warning { get { return new LogCategory("Warning"); } }
public static LogCategory Error { get { return new LogCategory("Error"); } }
}
传递类型安全的字符串值作为参数:
public static void Write(string message, LogCategory logCategory)
{
var log = new LogEntry { Message = message };
Logger.Write(log, logCategory.Value);
}
用法:
Logger.Write("This is almost like an enum.", LogCategory.Info);
ToString
return方法Value
。然后为字符串提供隐式强制转换运算符。public static implicit operator String(LogCategory category) { return Value; }
。
您还可以使用扩展模型:
public enum MyEnum
{
[Description("String 1")]
V1= 1,
[Description("String 2")]
V2= 2
}
您的延伸班
public static class MyEnumExtensions
{
public static string ToDescriptionString(this MyEnum val)
{
DescriptionAttribute[] attributes = (DescriptionAttribute[])val
.GetType()
.GetField(val.ToString())
.GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : string.Empty;
}
}
用法:
MyEnum myLocal = MyEnum.V1;
print(myLocal.ToDescriptionString());
public static string ToDescriptionString(this Enum ...
ie而不显式键入MyEnum
。
如何使用带有常量的静态类?
static class GroupTypes
{
public const string TheGroup = "OEM";
public const string TheOtherGroup = "CMB";
}
void DoSomething(string groupType)
{
if(groupType == GroupTypes.TheGroup)
{
// Be nice
}
else if (groupType == GroupTypes.TheOtherGroup)
{
// Continue to be nice
}
else
{
// unexpected, throw exception?
}
}
GroupTypes
作为参数类型传递,因为它是静态类。这就是Even Mien的答案所解决的问题。在这种情况下,您必须具有void DoSomething(string groupType)
,这意味着它groupType
可以具有任何字符串值,甚至包括您不期望的值,这意味着您必须为那些无效的类型做好准备,并决定如何处理它们(例如引发异常)。甚至Mien的答案也可以通过将有效输入的数量限制为LogCategory
类定义的选项来解决。
您可以将属性添加到枚举中的项目,然后使用反射从属性中获取值。
您将必须使用“字段”说明符来应用属性,如下所示:
enum GroupTypes
{
[field:Description("OEM")]
TheGroup,
[field:Description("CMB")]
TheOtherGroup
}
然后,您将对枚举类型的静态字段(在本例中为GroupTypes)DescriptionAttribute
进行反思,并使用反射获取要查找的值:
public static DescriptionAttribute GetEnumDescriptionAttribute<T>(
this T value) where T : struct
{
// The type of the enum, it will be reused.
Type type = typeof(T);
// If T is not an enum, get out.
if (!type.IsEnum)
throw new InvalidOperationException(
"The type parameter T must be an enum type.");
// If the value isn't defined throw an exception.
if (!Enum.IsDefined(type, value))
throw new InvalidEnumArgumentException(
"value", Convert.ToInt32(value), type);
// Get the static field for the value.
FieldInfo fi = type.GetField(value.ToString(),
BindingFlags.Static | BindingFlags.Public);
// Get the description attribute, if there is one.
return fi.GetCustomAttributes(typeof(DescriptionAttribute), true).
Cast<DescriptionAttribute>().SingleOrDefault();
}
DescriptionAttribute
如果您希望能够确定该属性是否已应用,则选择返回上面的自身。
实际上,您可以轻松地做到这一点。使用以下代码。
enum GroupTypes
{
OEM,
CMB
};
然后,当您要获取每个枚举元素的字符串值时,只需使用以下代码即可。
String oemString = Enum.GetName(typeof(GroupTypes), GroupTypes.OEM);
过去,我已经成功使用了此方法,并且还使用了一个常量类来保存字符串常量,两者都工作得很好,但是我倾向于这种方法。
尝试将常量添加到静态类。您不会以Type结尾,但是您将拥有可读的,有组织的常量:
public static class GroupTypes {
public const string TheGroup = "OEM";
public const string TheOtherGroup = "CMB";
}
为您的数据库创建另一个枚举,其中包含以下内容:
enum DBGroupTypes
{
OEM = 0,
CMB = 1
}
现在,您可以使用Enum.Parse从字符串“ OEM”和“ CMB”中检索正确的DBGroupTypes值。然后,您可以将其转换为int,并从要在模型中进一步使用的正确枚举中检索正确的值。
使用课程。
编辑:更好的例子
class StarshipType
{
private string _Name;
private static List<StarshipType> _StarshipTypes = new List<StarshipType>();
public static readonly StarshipType Ultralight = new StarshipType("Ultralight");
public static readonly StarshipType Light = new StarshipType("Light");
public static readonly StarshipType Mediumweight = new StarshipType("Mediumweight");
public static readonly StarshipType Heavy = new StarshipType("Heavy");
public static readonly StarshipType Superheavy = new StarshipType("Superheavy");
public string Name
{
get { return _Name; }
private set { _Name = value; }
}
public static IList<StarshipType> StarshipTypes
{
get { return _StarshipTypes; }
}
private StarshipType(string name, int systemRatio)
{
Name = name;
_StarshipTypes.Add(this);
}
public static StarshipType Parse(string toParse)
{
foreach (StarshipType s in StarshipTypes)
{
if (toParse == s.Name)
return s;
}
throw new FormatException("Could not parse string.");
}
}
解决该问题的另一种方法是拥有一个枚举和一个字符串数组,这些字符串将把枚举值与字符串列表进行映射:
public enum GroupTypes
{
TheGroup = 0,
TheOtherGroup
}
string[] GroupTypesStr = {
"OEM",
"CMB"
};
您可以这样使用它:
Log.Write(GroupTypesStr[(int)GroupTypes.TheOtherGroup]);
它将提示CMB
优点:
缺点:
这是我用来获取枚举值作为字符串的扩展方法。首先是枚举。
public enum DatabaseEnvironment
{
[Description("AzamSharpBlogDevDatabase")]
Development = 1,
[Description("AzamSharpBlogQADatabase")]
QualityAssurance = 2,
[Description("AzamSharpBlogTestDatabase")]
Test = 3
}
Description属性来自System.ComponentModel。
这是我的扩展方法:
public static string GetValueAsString(this DatabaseEnvironment environment)
{
// get the field
var field = environment.GetType().GetField(environment.ToString());
var customAttributes = field.GetCustomAttributes(typeof (DescriptionAttribute), false);
if(customAttributes.Length > 0)
{
return (customAttributes[0] as DescriptionAttribute).Description;
}
else
{
return environment.ToString();
}
}
现在,您可以使用以下代码将枚举作为字符串值访问:
[TestFixture]
public class when_getting_value_of_enum
{
[Test]
public void should_get_the_value_as_string()
{
Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString());
}
}
您是否考虑过使用字典查找表?
enum GroupTypes
{
TheGroup,
TheOtherGroup
}
Dictionary<string, GroupTypes> GroupTypeLookup = new Dictionary<string, GroupTypes>();
// initialize lookup table:
GroupTypeLookup.Add("OEM", TheGroup);
GroupTypeLookup.Add("CMB", TheOtherGroup);
然后,可以在读取字符串时使用GroupTypeLookup.TryGetValue()查找字符串。
public class DataType
{
private readonly string value;
private static readonly Dictionary<string, DataType> predefinedValues;
public static readonly DataType Json = new DataType("json");
public static readonly DataType Xml = new DataType("xml");
public static readonly DataType Text = new DataType("text");
public static readonly DataType Html = new DataType("html");
public static readonly DataType Binary = new DataType("binary");
static DataType()
{
predefinedValues = new Dictionary<string, DataType>();
predefinedValues.Add(Json.Value, Json);
predefinedValues.Add(Xml.Value, Xml);
predefinedValues.Add(Text.Value, Text);
predefinedValues.Add(Html.Value, Html);
predefinedValues.Add(Binary.Value, Binary);
}
private DataType(string value)
{
this.value = value;
}
public static DataType Parse(string value)
{
var exception = new FormatException($"Invalid value for type {nameof(DataType)}");
if (string.IsNullOrEmpty(value))
throw exception;
string key = value.ToLower();
if (!predefinedValues.ContainsKey(key))
throw exception;
return predefinedValues[key];
}
public string Value
{
get { return value; }
}
}
我只是创建一个字典,然后将代码用作键。
编辑:要解决有关进行反向查找(查找关键字)的评论,这将不会非常有效。如果有必要,我将编写一个新类来处理它。
我的第一个问题-您是否有权访问数据库?最好在数据库中对此进行规范化,否则,任何解决方案都将容易出错。以我的经验,随着时间的流逝,充满“ OEM”和“ CMB”的数据域往往会混入诸如“ oem”和其他“废数据”之类的东西。如果可以对其进行规范化,则可以使用密钥在包含作为枚举的元素的表中,您已经完成了,并且结构更加简洁。
如果那不可用,我将创建您的Enum,并创建一个类以为您将字符串解析为Enum。与使用Enum.Parse / Reflection / etc进行任何变通方法相比,这至少将为您提供处理非标准条目的灵活性,并为捕获或处理错误提供更大的灵活性。字典可以工作,但是如果遇到案例问题,字典可能会崩溃。
我建议编写一个类,以便您可以执行以下操作:
// I renamed this to GroupType, since it sounds like each element has a single type...
GroupType theType = GroupTypeParser.GetGroupType(theDBString);
这样可以保留大多数可读性,而无需更改数据库。
如果我理解正确,则需要从字符串转换为枚举:
enum GroupTypes {
Unknown = 0,
OEM = 1,
CMB = 2
}
static GroupTypes StrToEnum(string str){
GroupTypes g = GroupTypes.Unknown;
try {
object o = Enum.Parse(typeof(GroupTypes), str, true);
g = (GroupTypes)(o ?? 0);
} catch {
}
return g;
}
// then use it like this
GroupTypes g1 = StrToEnum("OEM");
GroupTypes g2 = StrToEnum("bad value");
如果需要,可以使用泛型来枚举枚举类型。
在VS 2015中,您可以使用nameof
public class LogCategory
{
public static string Trace;
public static string Debug;
public static string Info;
public static string Warning;
public static string Error;
}
用法:
Logger.Write("This is almost like an enum.", nameof(LogCategory.Info));
对Glennular扩展方法进行了一些细微调整,因此您可以将扩展程序用于除ENUM之外的其他功能;
using System;
using System.ComponentModel;
namespace Extensions {
public static class T_Extensions {
/// <summary>
/// Gets the Description Attribute Value
/// </summary>
/// <typeparam name="T">Entity Type</typeparam>
/// <param name="val">Variable</param>
/// <returns>The value of the Description Attribute or an Empty String</returns>
public static string Description<T>(this T t) {
DescriptionAttribute[] attributes = (DescriptionAttribute[])t.GetType().GetField(t.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
return attributes.Length > 0 ? attributes[0].Description : string.Empty;
}
}
}
或使用Linq
using System;
using System.ComponentModel;
using System.Linq;
namespace Extensions {
public static class T_Extensions {
public static string Description<T>(this T t) =>
((DescriptionAttribute[])t
?.GetType()
?.GetField(t?.ToString())
?.GetCustomAttributes(typeof(DescriptionAttribute), false))
?.Select(a => a?.Description)
?.FirstOrDefault()
?? string.Empty;
}
}
遵循@Even Mien的回答后,我尝试更进一步并使其通用(Generic),我似乎差不多了,但仍然有一种情况可以抗拒,并且我可能可以简化我的代码。
如果有人看到我可以改进的地方,特别是使其无法工作,因为我无法从字符串中分配它,我将其张贴在这里
到目前为止,我得到以下结果:
Console.WriteLine(TestEnum.Test1);//displays "TEST1"
bool test = "TEST1" == TestEnum.Test1; //true
var test2 = TestEnum.Test1; //is TestEnum and has value
string test3 = TestEnum.Test1; //test3 = "TEST1"
var test4 = TestEnum.Test1 == TestEnum.Test2; //false
EnumType<TestEnum> test5 = "TEST1"; //works fine
//TestEnum test5 = "string"; DOESN'T compile .... :(:(
魔术发生的地方:
public abstract class EnumType<T> where T : EnumType<T>
{
public string Value { get; set; }
protected EnumType(string value)
{
Value = value;
}
public static implicit operator EnumType<T>(string s)
{
if (All.Any(dt => dt.Value == s))
{
Type t = typeof(T);
ConstructorInfo ci = t.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,null, new Type[] { typeof(string) }, null);
return (T)ci.Invoke(new object[] {s});
}
else
{
return null;
}
}
public static implicit operator string(EnumType<T> dt)
{
return dt?.Value;
}
public static bool operator ==(EnumType<T> ct1, EnumType<T> ct2)
{
return (string)ct1 == (string)ct2;
}
public static bool operator !=(EnumType<T> ct1, EnumType<T> ct2)
{
return !(ct1 == ct2);
}
public override bool Equals(object obj)
{
try
{
return (string)obj == Value;
}
catch
{
return false;
}
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
public static IEnumerable<T> All
=> typeof(T).GetProperties()
.Where(p => p.PropertyType == typeof(T))
.Select(x => (T)x.GetValue(null, null));
}
我只需要为我的枚举声明一下即可:
public class TestEnum : EnumType<TestEnum>
{
private TestEnum(string value) : base(value)
{}
public static TestEnum Test1 { get { return new TestEnum("TEST1"); } }
public static TestEnum Test2 { get { return new TestEnum("TEST2"); } }
}
.Net Core 3.0 / C#8.0中的新增功能(如果您的工作环境允许您升级项目)是一种简短的switch语句,看起来有些枚举。归根结底,这是我们多年以来一直在使用的旧无聊的switch语句。
这里唯一的真正区别是switch语句有了新的用法。
public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
Rainbow.Red => new RGBColor(0xFF, 0x00, 0x00),
Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
Rainbow.Green => new RGBColor(0x00, 0xFF, 0x00),
Rainbow.Blue => new RGBColor(0x00, 0x00, 0xFF),
Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
_ => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
};
您会注意到,上面从此处复制的代码实际上是使用枚举作为参数。
这并不是您真正想要的(并且相信我,我一直想要类似于OP所要求的东西),但实际上我感觉这有点像MS的橄榄枝。JMO。
希望它能对某人有所帮助!
我使用了上一个答案中提到的结构,但是消除了任何复杂性。对我而言,这最像是创建字符串枚举。它的使用方式与枚举相同。
struct ViewTypes
{
public const string View1 = "Whatever string you like";
public const string View2 = "another string";
}
使用示例:
switch( some_string_variable )
{
case ViewTypes.View1: /* do something */ break;
case ViewTypes.View2: /* do something else */ break;
}
我什至按照@Even的建议(通过class X
和public static X
成员)实现了一些枚举,只是后来才发现,从.Net 4.5开始,这几天是正确的 ToString()
方法。
现在,我将所有实现重新实现为枚举。
这是将其用作强类型参数或字符串的一种方式:
public class ClassLikeEnum
{
public string Value
{
get;
private set;
}
ClassLikeEnum(string value)
{
Value = value;
}
public static implicit operator string(ClassLikeEnum c)
{
return c.Value;
}
public static readonly ClassLikeEnum C1 = new ClassLikeEnum("RandomString1");
public static readonly ClassLikeEnum C2 = new ClassLikeEnum("RandomString2");
}
您可以使用两个枚举。一个用于数据库,另一个用于可读性。
您只需要确保它们保持同步即可,这似乎是很小的代价。您不必设置值,只需将位置设置为相同即可,但是设置值可以使两个枚举很明显地相互关联,并防止错误地重新排列枚举成员。并通过注释使维护人员知道这些相关并且必须保持同步。
// keep in sync with GroupTypes
public enum GroupTypeCodes
{
OEM,
CMB
}
// keep in sync with GroupTypesCodes
public enum GroupTypes
{
TheGroup = GroupTypeCodes.OEM,
TheOtherGroup = GroupTypeCodes.CMB
}
要使用它,您只需先转换为代码:
GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = ((GroupTypeCodes)myGroupType).ToString();
然后,如果您想使其更加方便,可以添加仅适用于此类枚举的扩展功能:
public static string ToString(this GroupTypes source)
{
return ((GroupTypeCodes)source).ToString();
}
然后您可以执行以下操作:
GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = myGroupType.ToString();
enum
一个预期值的更改可能会无意间使另一个混乱。
我基本上是在寻找@ArthurC的思考答案
只需稍微扩展一下他的答案,您就可以通过使用泛型函数使其变得更好:
// If you want for a specific Enum
private static string EnumStringValue(GroupTypes e)
{
return EnumStringValue<GroupTypes>(e);
}
// Generic
private static string EnumStringValue<T>(T enumInstance)
{
return Enum.GetName(typeof(T), enumInstance);
}
然后,您可以将任何东西包裹起来
EnumStringValue(GroupTypes.TheGroup) // if you incorporate the top part
要么
EnumStringValue<GroupTypes>(GroupTypes.TheGroup) // if you just use the generic
摘自@EvenMien,并添加了一些评论。(也用于我自己的用例)
public struct AgentAction
{
private AgentAction(string value) { Value = value; }
public string Value { get; private set; }
public override string ToString()
{
return this.Value;
}
public static AgentAction Login = new AgentAction("Logout");
public static AgentAction Logout = new AgentAction("Logout");
public static implicit operator string(AgentAction action) { return action.ToString(); }
}
添加课程
public class DatabasePreference {
public DatabasePreference([CallerMemberName] string preferenceName = "") {
PreferenceName = preferenceName;
}
public string PreferenceName;
}
这项工作正在使用 CallerMemberName
用来最大程度地减少编码
使用:
//Declare names
public static DatabasePreference ScannerDefaultFlashLight = new DatabasePreference();
public static DatabasePreference ScannerQrCodes = new DatabasePreference();
public static DatabasePreference Scanner1dCodes = new DatabasePreference();
测试一下:
Console.WriteLine(ScannerDefaultFlashLight.PreferenceName);
Console.WriteLine(ScannerDefaultFlashLight.Scanner1dCodes);
输出:
ScannerDefaultFlashLight
Scanner1dCodes
根据其他意见,这就是我的想法。这种方法避免了在要获取常量值的位置键入.Value的方法。
我有一个像这样的所有字符串枚举的基类:
using System;
using Newtonsoft.Json;
[JsonConverter(typeof(ConstantConverter))]
public class StringEnum: IConvertible
{
public string Value { get; set; }
protected StringEnum(string value)
{
Value = value;
}
public static implicit operator string(StringEnum c)
{
return c.Value;
}
public string ToString(IFormatProvider provider)
{
return Value;
}
public TypeCode GetTypeCode()
{
throw new NotImplementedException();
}
public bool ToBoolean(IFormatProvider provider)
{
throw new NotImplementedException();
}
//The same for all the rest of IConvertible methods
}
JsonConverter是这样的:
using System;
using Newtonsoft.Json;
class ConstantConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null)
{
serializer.Serialize(writer, null);
}
else
{
serializer.Serialize(writer, value.ToString());
}
}
}
实际的字符串枚举将如下所示:
public sealed class Colors : StringEnum
{
public static Colors Red { get { return new Catalog("Red"); } }
public static Colors Yellow { get { return new Catalog("Yellow"); } }
public static Colors White { get { return new Catalog("White"); } }
private Colors(string value) : base(value) { }
}
有了这个,您可以使用Color.Red甚至序列化为json,而无需使用Value属性
我不需要像将字符串存储在属性中那样强大的功能。我只需要将类似的内容MyEnum.BillEveryWeek
变成“每周收费”或MyEnum.UseLegacySystem
“使用旧有系统”,基本上就是将骆驼的外壳分为小写的单个枚举。
public static string UnCamelCase(this Enum input, string delimiter = " ", bool preserveCasing = false)
{
var characters = input.ToString().Select((x, i) =>
{
if (i > 0 && char.IsUpper(x))
{
return delimiter + x.ToString(CultureInfo.InvariantCulture);
}
return x.ToString(CultureInfo.InvariantCulture);
});
var result = preserveCasing
? string.Concat(characters)
: string.Concat(characters).ToLower();
var lastComma = result.LastIndexOf(", ", StringComparison.Ordinal);
if (lastComma > -1)
{
result = result.Remove(lastComma, 2).Insert(lastComma, " and ");
}
return result;
}
MyEnum.UseLegacySystem.UnCamelCase()
输出“使用旧版系统”
如果设置了多个标志,它将变为纯英语(用逗号分隔,但用“和”代替最后一个逗号)。
var myCustomerBehaviour = MyEnum.BillEveryWeek | MyEnum.UseLegacySystem | MyEnum.ChargeTaxes;
Console.WriteLine(myCustomerBehaviour.UnCamelCase());
//outputs "bill every week, use legacy system and charge taxes"