有没有办法使用单个字符获取枚举的字符串值?

时间:2018-05-23 08:10:18

标签: c# enums

我在MVC应用程序中使用了一个枚举,它看起来像下面的枚举,

public enum School
{
    DVB=1,
    AVD=2,
    ASB=3
}

我从每个学校获得数据库中唯一的单个字符,例如得到:

D for DVB
A for AVD
B for ASB

我想打印枚举值的全名。我可以通过以下方式实现我的目标,即我可以编辑枚举并按字符替换枚举的整数值。但我不认为它是一个很好的解决方案,因为现有代码也使用整数值。

有没有其他方法可以使用单个字符获取枚举的字符串值?

3 个答案:

答案 0 :(得分:2)

最干净的方法是与您的团队一起计划并接受迁移数据库并存储整数值。

无论如何,根据您在问题中的评论,您正在使用实体框架并将您的实体映射到视图模型。以下步骤可以帮助您无需反思,添加新属性或新枚举。

假设您正在使用名为MyEntity的实体,其实际上包含名为MySchool的属性。

首先,不要将School枚举用于类型MySchool属性,而只使用字符串类型。所以EF只会从数据库中检索数据并将其直接放到属性中。无需转换。所以你会有这样的事情:

public class MyEntity
{
    public string MySchool { get; set; }
}

其次,总是在你的MyEntity类中添加另一个属性,让它命名为MySchoolEnum,它的类型为School枚举,而不是映射到数据库,所以你有这个:

[NotMapped] // <-- This tell EF that this property is not mapped to the database.
public School? MyShcoolEnum
{
    get
    {
        // Getting the value of this property depends on the value of the database
        // which is stored into MySchool property.
        switch (MySchool)
        {
            case "D": return School.DVB;
            case "A": return School.AVD;
            case "B": return School.ASB;
            default: return null;
        }
    }

    set
    {
        // When setting this property 
        // you automatically update the value of MySchool property
        switch (value)
        {
            case School.DVB: this.MySchool = "D";
                break;
            case School.AVD: this.MySchool = "A";
                break;
            case School.ASB: this.MySchool = "B";
                break;
            default: this.MySchool = null;
                break;
        }
    }
}

旁注:使用EF Core,有一种更优雅的方式可以在不添加新属性的情况下执行此类操作。使用EF Core 2.1,我们可以删除此属性并使用Value Conversion

最后,所有需要处理数据库MySchool列的视图模型都不应使用MySchool属性,而应将其对应的属性映射到实体的MyShcoolEnum属性。

您的实体代码应与此类似:

public class MyEntity
{
    public string MySchool { get; set; }

    [NotMapped] // <-- This tell EF that this property is not mapped to the database.
    public School? MyShcoolEnum
    {
        get
        {
            // Getting the value of this property depends on the value of the database
            // which is stored into MySchool property.
            switch (MySchool)
            {
                case "D": return School.DVB;
                case "A": return School.AVD;
                case "B": return School.ASB;
                default: return null;
            }
        }

        set
        {
            // When setting this property you automatically update the value of MySchool property
            switch (value)
            {
                case School.DVB: this.MySchool = "D";
                    break;
                case School.AVD: this.MySchool = "A";
                    break;
                case School.ASB: this.MySchool = "B";
                    break;
                default: this.MySchool = null;
                    break;
            }
        }
    }
}

答案 1 :(得分:1)

一个非常简单的课程,用于装饰&#34;具有[MapTo("x")]属性的属性值:

[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
public class MapToAttribute : Attribute
{
    public readonly string Key;

    public MapToAttribute(string key)
    {
        Key = key;
    }
}

public static class MapToUtilities<T> where T : struct
{
    private static readonly Dictionary<string, T> keyValues = Init();

    private static Dictionary<string, T> Init()
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(nameof(T));
        }

        var values = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Static);

        var keyValues2 = new Dictionary<string, T>();

        foreach (FieldInfo fi in values)
        {
            var attr = fi.GetCustomAttribute<MapToAttribute>();
            if (attr == null)
            {
                continue;
            }

            string key = attr.Key;

            T value = (T)fi.GetValue(null);

            keyValues2.Add(key, value);
        }

        return keyValues2;
    }

    public static T KeyToValue(string key)
    {
        return keyValues[key];
    }

    public static string ValueToKey(T value)
    {
        var cmp = EqualityComparer<T>.Default;
        return keyValues.First(x => cmp.Equals(x.Value, value)).Key;
    }
}

用法:

public enum School
{
    [MapTo("D")]
    DVB = 1,
    [MapTo("A")]
    AVD = 2,
    [MapTo("B")]
    ASB = 3
}

然后:

School sc = MapToUtilities<School>.KeyToValue("A");
string key = MapToUtilities<School>.ValueToKey(sc);

答案 2 :(得分:1)

有很多方法可以实现这一目标。一个非常简单的选择是使用您的值的替代ID的第二个枚举; e.g。

void Main()
{
    Console.WriteLine((School)Enum.Parse(typeof(ConvertSchool),"A"));
}
public enum School
{
    DVB=1,
    AVD=2,
    ASB=3
}
public enum ConvertSchool
{
    D =1,
    A =2,
    B =3
}

更好的是创建某种映射;例如使用字典将每个char键映射到相关的学校值:

public enum School
{
    DVB=1,
    AVD=2,
    ASB=3
}
readonly IDictionary SchoolMap = new Dictionary<char,School>() {
    {'D', School.DVB},
    {'A', School.AVD},
    {'B', School.ASB}
};
void Main()
{
    Console.WriteLine(SchoolMap['A']);
}

尽管如此,这些都容易出错(例如,如果有人在没有制作新的ConvertSchool条目/字典映射的情况下创建了新学校)。更好的方法是从enum切换到class,例如

void Main()
{
    Console.WriteLine((School)'A');
}
public class School
{
    //list of enums
    public static readonly List<School> Values = new List<School>();
    //"enum" values
    public static readonly School DVB = new School("DVB",'D',1);
    public static readonly School AVD = new School("AVD",'A',2);
    public static readonly School ASB = new School("ASB",'B',3);
    //properties
    public string Name {get;private set;}
    public char Code {get;private set;}
    public int Index {get;private set;}
    //constructor
    private School (string name, char code, int index)
    {
        if (Values.Exists(x => x.Name.Equals(name)) || Values.Exists(x => x.Code.Equals(code)) || Values.Exists(x => x.Index.Equals(index)))
        {
            throw new ArgumentException(string.Format("The Name, Code, and Index of each School value must be unique.  \nName: '{0}'\nSchool: '{1}'\nIndex: {2}", name, code, index));
        }
        Name = name;
        Code = code;
        Index = index;
        Values.Add(this);
    }
    //implicit conversion
    public static implicit operator School(string schoolName)
    {
        return Values.First(x => x.Name.Equals(schoolName));
    }
    public static implicit operator School(char schoolCode)
    {
        return Values.First(x => x.Code.Equals(schoolCode));
    }
    public static implicit operator School(int index)
    {
        return Values.First(x => x.Index.Equals(index));
    }
    //how should it be displayed?
    public override string ToString()
    {
        return Name;
    }
    //whatever other logic you need
}

在最后一个例子中,如果传递的字符没有相应的学校(例如Console.WriteLine((School)'X');),则会得到InvalidOperationException。如果您不希望在此处出现错误,但希望获得null值,请在每个隐式转换语句中将First替换为FirstOrDefault

另一种解决方案是在数据库层中保存映射;要么创建具有映射值的表,要么在视图的定义中保存这些映射;然后使用加入映射的表的视图来检索3个char代码,而不是每个学校的1个char代码;例如

--if you have a SchoolMap table relating the 1 char code to the 3 char code:

create table SchoolMap 
(
    Char1Code nchar(1) not null primary key clustered
    , Char3Code nvarchar(3) not null unique
)

create view SchoolView as 
select School.*
, SchoolMap.Char3Code
from School
left outer join SchoolMap 
on SchoolMap.Char1Code = School.Code 


--if you don't have and don't want a table holding the mapping, you can do the same in the view's definition:

create view SchoolView as 
select School.*
, SchoolMap.Char3Code
from School
left outer join 
(
    values ('D','DVB'),('A','AVD'),('B','ASB')
) SchoolMap (Char1Code, Char3Code)
on SchoolMap.Char1Code = School.Code