เชื่อมโยง enums กับสตริงใน C #


362

ฉันรู้ว่าสิ่งต่อไปนี้เป็นไปไม่ได้เพราะประเภทของการแจงนับต้องเป็น int

enum GroupTypes
{
    TheGroup = "OEM",
    TheOtherGroup = "CMB"
}

จากฐานข้อมูลของฉันฉันได้รับฟิลด์ที่มีรหัสที่ไม่ครอบคลุม( OEMและCMB) ฉันต้องการทำให้ฟิลด์นี้เป็นenumสิ่งที่เข้าใจได้ เพราะถ้าเป้าหมายนั้นสามารถอ่านได้การแก้ปัญหาควรจะสั้น

ฉันมีตัวเลือกอื่น ๆ อีกบ้าง?


สำเนาซ้ำของEnum ToString
nawfal

12
ฉันไม่แน่ใจว่าทำไมคำตอบส่วนใหญ่ไม่เพียงแค่ใช้ "const string" แต่พวกเขากำลังสร้างคลาสที่กำหนดเองแทน
CTS_AE

1
คุณอาจไม่สามารถใช้สตริงได้ แต่คุณสามารถใช้ตัวอักษรได้ดี นั่นเป็นตัวเลือกถ้าคุณสามารถใช้ค่าตัวอักษรเดียว
ต. ส.

1
สับสนอย่างแท้จริงว่าทำไมคำตอบที่เสนอข้างต้นโดย CTS_AE ไม่ได้อยู่ในคำตอบสามอันดับแรก
สินใจ

@Sinjai การจัดกลุ่มค่าที่เกี่ยวข้องอย่างชัดเจนจะมีค่ามากกว่าการลงโทษที่สูญเสียประสิทธิภาพโดยเฉพาะใน API หรือส่วนประกอบที่นำกลับมาใช้ใหม่
person27

คำตอบ:


403

ฉันชอบที่จะใช้คุณสมบัติในชั้นเรียนแทนวิธีการเนื่องจากพวกเขามีลักษณะเหมือน enum มากขึ้น

นี่คือตัวอย่างสำหรับ Logger:

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);

4
ข้อเสียอย่างเดียวที่ฉันคิดได้คือว่ามันจะช้ากว่านิดหน่อย แต่ในกรณีนี้ส่วนใหญ่จะถูกทอดทิ้ง และมันจะไม่มีพฤติกรรมแบบเดียวกันในเครื่องมือแก้ไข EG: การสลับไปที่สิ่งนี้จะไม่กรอกข้อมูลในแต่ละกรณีโดยอัตโนมัติ นอกเหนือจากประเด็นย่อย ๆ เหล่านั้นฉันคิดว่านี่อาจเป็นทางออกที่ค่อนข้างง่าย
บบส.

3
และมันง่ายที่จะใช้พจนานุกรม <LogCategory, Action / Func> เป็นสวิตช์ :)
Arnis Lapsa

4
@ArnisL การทำงานเป็นคีย์ไม่เพียงพอคุณต้องแทนที่ Equals () และ GetHashCode () และคุณต้องการทำให้ setter คุณสมบัติ Value เป็นส่วนตัว ถึงกระนั้นมันก็ไม่ได้เป็น Enum
Dave Van den Eynde

21
สำหรับการใช้งานของตัวเองผมขยายความแนวคิดนี้เอาชนะวิธีการที่จะกลับมาToString Valueและจากนั้นให้ตัวดำเนินการส่งโดยนัยไปยังและจากสตริง public static implicit operator String(LogCategory category) { return Value; }.
Zarepheth

6
สิ่งที่เกี่ยวกับการใช้สิ่งนี้ในกรณีสวิตช์?
เดวิด

176

คุณสามารถใช้โมเดลส่วนขยาย:

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());

3
ดูstackoverflow.com/questions/4367723/…สำหรับส่วนขยายอื่นและจากสตริงไปยัง enum ตามวิธีการอธิบาย
เดฟ

14
ฉันอดคิดไม่ได้ว่าการสะท้อน enum ทุกครั้งที่คุณต้องการจะแสดงข้อความเสียงที่เจ็บปวดจากมุมมองการแสดง!
Liath

4
@Liath - `ToString () 'ใช้การสะท้อนอยู่แล้วดังนั้นคุณจึงไม่สูญเสียอะไรกับวิธีการนี้และทำให้อ่านง่ายขึ้น
James King

1
คุณสามารถสร้างส่วนขยายทั่วไปได้หรือไม่ดังนั้นจึงใช้กับ Enums ทั้งหมดโดยอัตโนมัติ
erosebe

3
เพื่อให้ทั่วไปใช้คือไม่มีอย่างชัดเจนพิมพ์เพื่อpublic static string ToDescriptionString(this Enum ... MyEnum
LeeCambl

100

วิธีการเกี่ยวกับการใช้คลาสคงที่กับค่าคงที่?

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?
  }
}

9
ตกลง ฉันมีปัญหาในการมองเห็นจุดประสงค์ของการแก้ปัญหาที่ซับซ้อนมากขึ้นยกเว้นอาจจะสามารถเปลี่ยน "enum" ที่เกิดขึ้นได้
fakeleft

@ fakeleft คุณไม่สามารถใช้ประเภทคลาสคงที่กับประเภททั่วไป (เทมเพลต) และอาจมีข้อ จำกัด อื่น ๆ ฉันคิดว่านั่นเป็นเหตุผลที่ผู้คนต้องการโซลูชันที่ "ซับซ้อนมากขึ้น"
eselk

2
ค่าคงที่ต้องเป็นค่าภายในหรือสาธารณะเพื่อให้สามารถใช้งานได้
arviman

46
ประเภทสแตติกไม่สามารถใช้เป็นพารามิเตอร์ได้
Pedro Moreira

2
ในฐานะที่เป็น @PedroMoreira ชี้ให้เห็นคุณไม่สามารถผ่านGroupTypesเป็นประเภทอาร์กิวเมนต์ได้เนื่องจากเป็นคลาสแบบสแตติก นั่นคือปัญหาที่แม้แต่คำตอบของเมี่ยนก็แก้ปัญหาได้ ในกรณีนี้คุณต้องมีแทนvoid DoSomething(string groupType)ซึ่งหมายความว่าgroupTypeอาจมีค่าสตริงใด ๆแม้แต่ค่าที่คุณไม่ได้คาดหวังซึ่งหมายความว่าคุณต้องเตรียมพร้อมสำหรับประเภทที่ไม่ถูกต้องเหล่านั้นและตัดสินใจว่าจะจัดการกับมันอย่างไร (เช่น โดยการขว้างข้อยกเว้น) แม้แต่คำตอบของเมี่ยนก็แก้ปัญหาด้วยการ จำกัด จำนวนอินพุตที่ใช้ได้กับตัวเลือกที่กำหนดโดยLogCategoryคลาส
Pharap

30

คุณสามารถเพิ่มแอททริบิวให้กับรายการในการแจงนับจากนั้นใช้การสะท้อนเพื่อรับค่าจากแอททริบิวต์

คุณจะต้องใช้ตัวระบุ "ฟิลด์" เพื่อใช้คุณสมบัติเช่น:

enum GroupTypes
{
    [field:Description("OEM")]
    TheGroup,

    [field:Description("CMB")]
    TheOtherGroup
}

จากนั้นคุณจะไตร่ตรองในฟิลด์สแตติกของชนิดของ enum (ในกรณีนี้ 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ตัวเองด้านบนในกรณีที่คุณต้องการให้สามารถระบุได้ว่ามีการใช้คุณลักษณะนี้หรือไม่


แม้ว่าฉันจะจำสิ่งนี้สำหรับสถานการณ์ที่ซับซ้อนมากขึ้น แต่มันค่อนข้างซับซ้อนสำหรับสถานการณ์ที่มีระดับความซับซ้อนของสิ่งที่ฉันระบุไว้ใน OP
Boris Callens

26

คุณสามารถทำได้ง่ายมากจริง ๆ ใช้รหัสต่อไปนี้

enum GroupTypes
{
   OEM,
   CMB
};

จากนั้นเมื่อคุณต้องการรับค่าสตริงของแต่ละองค์ประกอบ enum เพียงใช้บรรทัดของรหัสต่อไปนี้

String oemString = Enum.GetName(typeof(GroupTypes), GroupTypes.OEM);

ฉันเคยใช้วิธีนี้ได้สำเร็จในอดีตและฉันยังใช้คลาสค่าคงที่เพื่อเก็บค่าคงที่สตริงทั้งสองทำงานออกมาได้ดี แต่ฉันมักจะชอบสิ่งนี้


ฉันกำลังคิดในสิ่งเดียวกัน แต่จะต้องมีการจับนี้ ... มิฉะนั้นฉันจะสงสัยว่าคนอื่นจะแนะนำนี้ (บางทีฉันแค่หวาดระแวง)
Matthijs Wessels

สิ่งเดียวที่ฉันรู้เรื่องนี้คือฉันเชื่อว่ามันใช้การไตร่ตรองเพื่อหาสตริง ดังนั้นถ้าฉันเพิ่งจะแก้ปัญหาเพื่อติดตามสตริงคงที่จากนั้นฉันมักจะใช้คลาสเพื่อเก็บสตริงคงที่ส่วนใหญ่ของฉัน อย่างไรก็ตามถ้าฉันมีสถานการณ์ที่ Enum เป็นวิธีการแก้ปัญหาที่ถูกต้อง (ไม่ว่าจะเป็นสตริงอธิบายเกี่ยวกับองค์ประกอบ Enum ของฉัน) แทนที่จะมีสตริงพิเศษที่ลอยอยู่รอบ ๆ เพื่อจัดการฉันก็แค่ใช้ค่า enum ตามที่อธิบายไว้
Arthur C

+1 นี่คือคำตอบที่ดีที่สุดและง่ายที่สุดและยังมีคะแนนโหวตสูงที่นี่เพื่อพิสูจน์ เวลาเท่านั้นที่จะดีกว่าที่จะใช้โมเดลส่วนขยายคือเมื่อคุณต้องการเว้นวรรคในข้อความ (รายละเอียดเพิ่มเติมที่นี่ )
SharpC

14
ไม่นี่เป็นเพียงการรับชื่อของค่า enum ไม่ได้กำหนดสตริงให้กับค่า enum เป้าหมายของ OP คือการมีสตริงที่แตกต่างจากค่า enum เช่น: TheGroup = "OEM", TheOtherGroup = "CMB"
Tim Autin

3
ฉันเห็นด้วยกับความคิดเห็นของ @ Tim นี่ไม่ใช่สิ่งที่ OP พยายามทำ หากคุณสงสัยว่ากรณีนี้คืออะไรให้พิจารณาสถานการณ์ที่อุปกรณ์ใช้สตริงเป็นคำสั่ง แต่ต้องมีคำสั่งเวอร์ชัน "ที่มนุษย์อ่านได้" ด้วยเช่นกัน ฉันต้องการสิ่งนี้เพื่อเชื่อมโยงบางอย่างเช่น "อัปเดตเฟิร์มแวร์" กับคำสั่ง "UPDATEFW"
JYelton

20

ลองเพิ่มค่าคงที่ให้กับคลาสแบบคงที่ คุณไม่ได้จบด้วย Type แต่คุณจะมีค่าคงที่ที่สามารถอ่านได้และจัดระเบียบได้:

public static class GroupTypes {

    public const string TheGroup = "OEM";
    public const string TheOtherGroup = "CMB";

}

3
ยากที่จะเปลี่ยนจากรหัสกลับไปเป็นชื่อที่สื่อความหมาย คุณจะต้องใช้การไตร่ตรองเหนือฟิลด์ const ทั้งหมดเพื่อค้นหาการจับคู่
andleer

1
@ andleer ฉันไม่เข้าใจความกังวลของคุณ นี่คือทางออกที่ฉันใช้
VSO

ใช่นี่คือสิ่งที่ฉันต้องการอย่างแท้จริง และนี่คือวิธีแก้ปัญหาที่กระชับ / หรูหราที่สุดที่ฉันเห็นราวกับว่าฉันกำลังกำหนดค่าการแจงนับ w / int - แต่มีค่าสตริงแทน สมบูรณ์แบบ 100%
ชาด

3
ปัญหาของเรื่องนี้คือมันไม่ทำงานเป็น Enum ในแง่ที่ว่าเราจะไม่แยกประเภทที่มีรายการค่าที่แน่นอน ฟังก์ชั่นที่คาดว่าสิ่งเหล่านี้สามารถใช้กับสตริงที่มีรูปแบบอิสระซึ่งเป็นข้อผิดพลาดได้ง่าย
Juan Martinez

14

สร้าง enum ที่สองสำหรับ DB ของคุณที่มีสิ่งต่อไปนี้:

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}

ตอนนี้คุณสามารถใช้ Enum.Parse เพื่อดึงค่า DBGroupTypes ที่ถูกต้องจากสตริง "OEM" และ "CMB" จากนั้นคุณสามารถแปลงค่าเหล่านั้นเป็น int และดึงค่าที่ถูกต้องจากการแจงนับที่ถูกต้องที่คุณต้องการใช้เพิ่มเติมในโมเดลของคุณ


นี่ดูเหมือนจะเป็นขั้นตอนพิเศษในกระบวนการทำไมไม่เรียนที่จัดการทุกอย่าง
C. Ross

11
ตรงข้ามกับการใช้คุณลักษณะและการสะท้อน?
Dave Van den Eynde

13

ใช้คลาส

แก้ไข: ตัวอย่างที่ดีกว่า

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.");
    }
}

1
ยากที่จะเปลี่ยนจากรหัสกลับไปเป็นชื่อที่สื่อความหมาย คุณจะต้องใช้การไตร่ตรองเหนือฟิลด์ const ทั้งหมดเพื่อค้นหาการจับคู่
andleer

1
ฉันเห็นประเด็นของคุณ ฉันจะอัปโหลดเวอร์ชันที่ใช้งานได้ในภายหลัง แต่ฉันยอมรับว่ามันหนักมาก
C. Ross

เวอร์ชันของฉันอ้างอิงจากโซลูชันของ C. Ross stackoverflow.com/a/48441114/3862615
Roman M

7

อีกวิธีในการจัดการกับปัญหาคือการมี enum และอาร์เรย์ของสตริงที่จะจับคู่ค่า enum กับรายการของสตริง:

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

string[] GroupTypesStr = {
    "OEM",
    "CMB"
};

คุณอาจใช้สิ่งนี้:

Log.Write(GroupTypesStr[(int)GroupTypes.TheOtherGroup]);

มันจะแจ้งให้ CMB

ข้อดี:

  1. รหัสที่ง่ายและสะอาด
  2. ประสิทธิภาพสูง (โดยเฉพาะเมื่อเปรียบเทียบกับวิธีการที่ใช้คลาส)

ข้อเสีย:

  1. มีแนวโน้มที่จะเลอะรายการเมื่อแก้ไข แต่จะไม่เป็นไรสำหรับรายการสั้น ๆ

6

นี่คือวิธีการขยายที่ฉันใช้เพื่อรับค่า enum เป็นสตริง ก่อนอื่นนี่คือ enum

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(); 
    }
}

ตอนนี้คุณสามารถเข้าถึง enum เป็นค่าสตริงโดยใช้รหัสต่อไปนี้:

[TestFixture]
public class when_getting_value_of_enum
{
    [Test]
    public void should_get_the_value_as_string()
    {
        Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString());  
    }
}

5

คุณพิจารณาตารางการค้นหาโดยใช้พจนานุกรมหรือไม่

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 () เพื่อค้นหาสตริงเมื่อคุณอ่าน


คุณจะรับกุญแจสำหรับค่าที่กำหนดได้อย่างไร?
eglasius

คำถามไม่ได้ถามว่าจะไปทางอื่น แต่มันจะง่ายพอที่จะสร้างพจนานุกรมอื่นที่ไปในทางอื่น นั่นคือพจนานุกรม <GroupTypes, สตริง>
Jim Mischel

4
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; }
    }
}

3

C # ไม่รองรับสตริงที่ระบุ แต่สำหรับสถานการณ์ส่วนใหญ่คุณสามารถใช้ List หรือ Dictionary เพื่อให้ได้เอฟเฟกต์ที่ต้องการ

เช่นการพิมพ์ผลลัพธ์ Pass / Fail:

List<string> PassFail = new List<string> { "FAIL", "PASS" };
bool result = true;
Console.WriteLine("Test1: " + PassFail[result.GetHashCode()]);

2

ฉันจะทำให้เป็นคลาสที่หลีกเลี่ยงการ enum ทั้งหมด และด้วยการใช้ typehandler คุณสามารถสร้างวัตถุเมื่อคุณคว้ามันจาก db

IE:

public class Group
{
    public string Value{ get; set; }
    public Group( string value ){ Value = value; } 
    public static Group TheGroup() { return new Group("OEM"); }
    public static Group OtherGroup() { return new Group("CMB"); }

}

2

ฉันแค่สร้างพจนานุกรมและใช้รหัสเป็นกุญแจ

แก้ไข: เพื่อระบุความคิดเห็นเกี่ยวกับการค้นหาแบบย้อนกลับ (การค้นหากุญแจ) สิ่งนี้จะไม่ได้มีประสิทธิภาพมากนัก หากจำเป็นฉันจะเขียนคลาสใหม่เพื่อจัดการกับมัน


นอกจากนี้คุณยังสามารถคว้ากุญแจเพื่อรับค่าได้อย่างง่ายดาย?
eglasius

สำหรับ C.Ross - ฉันไม่แน่ใจว่าคุณหมายถึงอะไร คุณสามารถอ่านค่าจากฐานข้อมูลและเติมพจนานุกรมแบบไดนามิก
jhale

2

คำถามแรกของฉัน - คุณสามารถเข้าถึงฐานข้อมูลได้หรือไม่? สิ่งนี้ควรได้รับการทำให้เป็นมาตรฐานในฐานข้อมูลไม่เช่นนั้นแล้วการแก้ปัญหาใด ๆ ก็จะเกิดข้อผิดพลาด จากประสบการณ์ของฉันเขตข้อมูลที่เต็มไปด้วย "OEM" และ "CMB" มักจะจบลงด้วยการมีสิ่งต่าง ๆ เช่น "oem" และ "ข้อมูลอึ" อื่น ๆ ผสมอยู่ตลอดเวลา .... ถ้าคุณสามารถทำให้ปกติคุณสามารถใช้ปุ่ม ในตารางที่มีองค์ประกอบเหมือนกับ Enum ของคุณและคุณทำเสร็จแล้วด้วยโครงสร้างที่สะอาดกว่ามาก

หากยังไม่พร้อมใช้งานฉันจะสร้าง Enum ของคุณและสร้างคลาสเพื่อแยกสตริงของคุณเป็น Enum ให้คุณ อย่างน้อยนี่จะให้ความยืดหยุ่นในการจัดการรายการที่ไม่ได้มาตรฐานและความยืดหยุ่นในการดักจับหรือการจัดการข้อผิดพลาดมากกว่าการแก้ไขปัญหาใด ๆ โดยใช้ Enum.Parse / Reflection / ฯลฯ พจนานุกรมอาจใช้งานได้ แต่อาจพังทลายถ้าคุณมีปัญหาเกี่ยวกับคดี ฯลฯ

ฉันขอแนะนำให้เขียนชั้นเรียนเพื่อให้คุณสามารถ:

// I renamed this to GroupType, since it sounds like each element has a single type...
GroupType theType = GroupTypeParser.GetGroupType(theDBString);

สิ่งนี้จะรักษาความสามารถในการอ่านส่วนใหญ่ของคุณโดยไม่ต้องเปลี่ยนฐานข้อมูล


2

หากฉันเข้าใจถูกต้องคุณต้องมีการแปลงจากสตริงเป็น enum:

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");

คุณสามารถทำให้แฟนซีมากขึ้นด้วยยาชื่อสามัญสำหรับประเภท Enum หากคุณต้องการ


2

ใน VS 2015 คุณสามารถใช้ชื่อของ

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));

2

ปรับแต่งขนาดเล็กไปยังวิธีการขยายแบบ Glennular ดังนั้นคุณสามารถใช้ส่วนขยายกับสิ่งอื่นนอกเหนือจากเพียงแค่ Enum's;

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;  
    }
}

2

ทำตามคำตอบของ @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"); } }
}

ขอบคุณสำหรับงานที่ยอดเยี่ยมนี้ฉันกำลังมองหาวิธีการดังกล่าวมานาน ฉันคิดว่าคุณควรได้รับ 1,000 คะแนนสำหรับเรื่องนี้
user3492977

โอ้ขอบคุณสำหรับความคิดเห็นนี้และขอบคุณที่เตือนฉันเกี่ยวกับเรื่องนี้ฉันไม่ได้ใช้ c # เป็นเวลาสองปีเมื่อฉันเขียนโค้ดนี้ฉันควรกลับไปใช้มันในไม่ช้า!
Lomithrani

@ user3492977 ในที่สุดฉันก็กลับไปหามันและทำให้มันทำงานได้อย่างสมบูรณ์ฉันยังคงสงสัยอยู่ว่าถ้ามันเป็นความคิดที่ดีหรือสิ่งที่ไร้ประโยชน์: D stackoverflow.com/questions/62043138/ …
Lomithrani

2

ใหม่ใน. Net Core 3.0 / C # 8.0 (หากสภาพแวดล้อมการทำงานของคุณอนุญาตให้คุณอัพเกรดโครงการของคุณ) เป็นคำสั่งสลับมือสั้นที่มีลักษณะค่อนข้าง enum-ish ในตอนท้ายของวันมันเป็นคำสั่งสลับที่น่าเบื่อเหมือนเดิมที่เราใช้มานานหลายปี

ข้อแตกต่างที่แท้จริงเท่านั้นที่นี่คือคำสั่ง 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)),
};

คุณจะสังเกตเห็นว่ารหัสด้านบนที่ฉันคัดลอกมาจากที่นี่จริง ๆ แล้วใช้ enum เป็นพารามิเตอร์

มันไม่ใช่สิ่งที่คุณต้องการ (และเชื่อใจฉันฉันต้องการบางสิ่งที่คล้ายกับที่ OP ร้องขอมาเป็นเวลานาน) แต่จริง ๆ แล้วฉันรู้สึกว่านี่เป็นกิ่งมะกอกจาก MS jmo

หวังว่ามันจะช่วยให้ใครบางคน!


2

ฉันใช้โครงสร้างที่พูดพาดพิงถึงคำตอบก่อนหน้า แต่ไม่ได้ซับซ้อนอะไรเลย สำหรับฉันนี่เป็นเหมือนการสร้างการนับจำนวนสตริง มันถูกใช้ในลักษณะเดียวกับที่ใช้ในการแจงนับ

    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;
   }

1

ฉันยังใช้ enums สองสามอย่างตามที่แนะนำโดย @Even (ผ่านclass Xและpublic static Xสมาชิก) เพียงเพื่อค้นหาภายหลังว่าวันนี้เริ่มต้น. Net 4.5 มีวิธีที่ถูกต้อง ToString()

ตอนนี้ฉันกำลังปรับปรุงทุกอย่างกลับไปเป็น enums


1

นี่คือวิธีใช้เป็นพารามิเตอร์ที่พิมพ์อย่างยิ่งหรือเป็นสตริง :

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");
}

1

คุณสามารถใช้สอง enums หนึ่งสำหรับฐานข้อมูลและอื่น ๆ สำหรับการอ่าน

คุณเพียงแค่ต้องทำให้แน่ใจว่าพวกเขายังคงซิงค์ซึ่งดูเหมือนว่ามีค่าใช้จ่ายเล็กน้อย คุณไม่จำเป็นต้องตั้งค่าเพียงแค่ตั้งค่าตำแหน่งเดียวกัน แต่การตั้งค่าทำให้ชัดเจนว่าทั้งสอง enums เกี่ยวข้องกันและป้องกันข้อผิดพลาดจากการจัดเรียงสมาชิก enum อีกครั้ง และความคิดเห็นช่วยให้ทีมงานซ่อมบำรุงรู้ว่าสิ่งเหล่านี้เกี่ยวข้องและจะต้องได้รับการซิงค์

// 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();

จากนั้นหากคุณต้องการทำให้สะดวกยิ่งขึ้นคุณสามารถเพิ่มฟังก์ชันส่วนขยายที่ใช้ได้กับ enum ประเภทนี้เท่านั้น:

public static string ToString(this GroupTypes source)
{
    return ((GroupTypeCodes)source).ToString();
}

จากนั้นคุณสามารถทำได้:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = myGroupType.ToString();

นั่นคือการปฏิบัติที่ไม่ดี: การพึ่งพาenumการเปลี่ยนแปลงมูลค่าที่ตั้งใจไว้ในอันใดอันหนึ่งอาจทำให้คนอื่นยุ่งเหยิงโดยไม่ตั้งใจ
Lorenz Lo Sauer

1

โดยทั่วไปฉันกำลังมองหาคำตอบ Reflection โดย @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

1

นำมาจาก @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(); }
}

1

การเพิ่มคลาสนี้

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

0

จากความคิดเห็นอื่น ๆ นี่คือสิ่งที่ฉันคิดขึ้นมา วิธีนี้หลีกเลี่ยงการพิมพ์. มูลค่าที่คุณต้องการรับค่าคงที่

ฉันมีชั้นฐานสำหรับทุก enums สตริงเช่นนี้

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());
        }
    }
}

และ enum สตริงจริงจะเป็นดังนี้:

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 โดยไม่ต้องใช้คุณสมบัติค่า


0

ฉันไม่ต้องการอะไรที่แข็งแกร่งเช่นการจัดเก็บสตริงไว้ในแอตทริบิวต์ ฉันแค่ต้องการเปลี่ยนบางสิ่งบางอย่าง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"
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.