Enum ToString ด้วยสตริงที่เป็นมิตรกับผู้ใช้


282

Enum ของฉันประกอบด้วยค่าต่อไปนี้:

private enum PublishStatusses{
    NotCompleted,
    Completed,
    Error
};

ฉันต้องการที่จะสามารถส่งออกค่าเหล่านี้ในทางที่เป็นมิตรกับผู้ใช้ว่า
ฉันไม่จำเป็นต้องเปลี่ยนจากสตริงเป็นค่าอีกครั้ง


สำเนาซ้ำได้ของC # String enums
nawfal

สำเนาซ้ำที่เป็นไปได้ของการเป็นตัวแทนของ Enum
Liam

คำตอบ:


350

ฉันใช้Descriptionคุณลักษณะจาก System.ComponentModel namespace เพียงตกแต่ง Enum:

private enum PublishStatusValue
{
    [Description("Not Completed")]
    NotCompleted,
    Completed,
    Error
};

จากนั้นใช้รหัสนี้เพื่อดึงข้อมูล:

public static string GetDescription<T>(this T enumerationValue)
    where T : struct
{
    Type type = enumerationValue.GetType();
    if (!type.IsEnum)
    {
        throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
    }

    //Tries to find a DescriptionAttribute for a potential friendly name
    //for the enum
    MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
    if (memberInfo != null && memberInfo.Length > 0)
    {
        object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

        if (attrs != null && attrs.Length > 0)
        {
            //Pull out the description value
            return ((DescriptionAttribute)attrs[0]).Description;
        }
    }
    //If we have no description attribute, just return the ToString of the enum
    return enumerationValue.ToString();
}

12
ตัวอย่างนี้ง่ายต่อการอ่าน stackoverflow.com/questions/1415140/…
RayLoveless

31
ฉันสงสัยว่ามีการใช้งานไตร่ตรองอย่างมากสำหรับการใช้การสะท้อนตามที่อธิบายไว้ในโซลูชันนี้ รหัสสำหรับวิธีการของ Will จะใช้วิธีการขยาย ToFriendlyString นั้นง่ายต่อการเข้าใจมากขึ้นและประสิทธิภาพก็ควรจะรวดเร็วเช่นกัน
humbads

1
ฉันชอบรุ่นที่ @RayL เชื่อมโยงเนื่องจากจะเพิ่มเฉพาะวิธีการส่วนขยายใน Enums เท่านั้น ถ้านั่นคือทั้งหมดที่คุณต้องการใช้นี้ (ตามที่ระบุไว้ด้วยArgumentExceptionแล้วมีเหตุผลที่จะมีวิธีการที่จะทั่วไปไม่สมบูรณ์.
krillgar

4
มันหมายความว่าทุก enum ต้องการวิธีการขยายของตัวเอง นี่เป็นการใช้งานทั่วไปมากขึ้นและต้องใช้งานมากขึ้น แต่คุณอาจต้องการหาปริมาณว่า "เร็ว" หมายถึงอะไรก่อนที่เราจะตัดสินใจเกี่ยวกับประสิทธิภาพ
Ray Booysen

2
@petar ที่ใช้งานได้ แต่ไม่ต้องการให้คุณแสดงสตริงที่เป็นมิตรต่อผู้ใช้ MY_TYPE จะมีขีดล่างและไม่สามารถปรับแต่งได้
Ray Booysen

354

ฉันทำได้ด้วยวิธีการขยาย:

public enum ErrorLevel
{
  None,
  Low,
  High,
  SoylentGreen
}

public static class ErrorLevelExtensions
{
  public static string ToFriendlyString(this ErrorLevel me)
  {
    switch(me)
    {
      case ErrorLevel.None:
        return "Everything is OK";
      case ErrorLevel.Low:
        return "SNAFU, if you know what I mean.";
      case ErrorLevel.High:
        return "Reaching TARFU levels";
      case ErrorLevel.SoylentGreen:
        return "ITS PEOPLE!!!!";
      default:
        return "Get your damn dirty hands off me you FILTHY APE!";
    }
  }
}

6
นี่เป็นคำตอบที่สะอาดกว่ามาก ดี!
pennyrave

3
@pennyrave: เอ๊ะ องค์ประกอบ UI จำนวนมากคาดหวังว่าจะค้นหาและใช้ DisplayNameAttribute และ DescriptionAttribute อันที่จริงตอนนี้ฉันใช้สิ่งเหล่านี้และวิธีการขยายเพื่อให้ได้ค่าเหล่านั้นออกมาได้อย่างง่ายดาย

60
ปัญหาที่ฉันเห็นคือคุณกำลังเขียนวิธีการขยายเหล่านี้อยู่ตลอดเวลา ด้วยกลไกการกำหนดคุณลักษณะมันเป็นวิธีที่เรียบง่ายในการตกแต่งและเรียกวิธีการเดียวเท่านั้น
Ray Booysen

5
ไม่แน่ใจคุณหมายถึงอะไร?
Ray Booysen

9
ในความคิดของฉันดีกว่าที่จะอนุญาตให้การdefaultดำเนินการกับเคสส่งคืนme.ToString()และจัดเตรียมคำสั่ง switch case สำหรับค่า enum ที่คุณต้องการแทนที่ ในตัวอย่างของคุณฉันได้ว่ามันแตกต่างกันทั้งหมด แต่ในกรณีการใช้งานจริงฉันสงสัยว่าค่า enum คำเดียวส่วนใหญ่จะพอเพียงและคุณจะให้การแทนที่ค่า enum หลายคำเท่านั้น
สกอตต์

78

บางทีฉันอาจจะพลาดบางอย่าง แต่เกิดอะไรขึ้นกับ Enum.GetName?

public string GetName(PublishStatusses value)
{
    return Enum.GetName(typeof(PublishStatusses), value)
}

แก้ไข: สำหรับสตริงที่เป็นมิตรกับผู้ใช้คุณต้องผ่าน .resource เพื่อให้การแปลเป็นภาษาท้องถิ่น / สากลและมันจะดีกว่าที่จะใช้รหัสคงที่ตามคีย์ enum มากกว่าแอตทริบิวต์มัณฑนากรในที่เดียวกัน


12
ฉันคืนค่าตัวอักษรของ enum ไม่ใช่ผู้ใช้ที่เป็นมิตร
Boris Callens

2
oic - ดีมีคดีใหญ่สวยที่คุณจะต้องไปผ่านทรัพยากรห้องสมุดสตริงขึ้นอยู่กับค่านี้แล้วเพราะทางเลือก (attribs มัณฑนากร) จะไม่สนับสนุน I18N
annakata

1
ในกรณีของ I18N ฉันจะทำการค้นหาวิธี GetDescription () ใน lib ทรัพยากรสำหรับสตริงที่แปลแล้วถอยกลับไปที่คำอธิบายแล้วถอยกลับไปที่ตัวอักษร
บบเรียกว่า

3
+1 สำหรับ MyEnum.ToString () เป็นรหัสทรัพยากรสำหรับการแปล ฉันทำแบบนั้นมาหลายปีแล้ว
jackvsworld

1
@annakata เราได้ขยายกลไกคุณลักษณะเพื่อรวมการสนับสนุนสำหรับ l18N จริง ๆ แล้วมันเป็นการเปลี่ยนแปลงที่เรียบง่ายในความเป็นจริง
Ray Booysen

23

ฉันสร้างวิธีการขยายย้อนกลับเพื่อแปลงคำอธิบายกลับเป็นค่า enum:

public static T ToEnumValue<T>(this string enumerationDescription) where T : struct
{
    var type = typeof(T);

    if (!type.IsEnum)
        throw new ArgumentException("ToEnumValue<T>(): Must be of enum type", "T");

    foreach (object val in System.Enum.GetValues(type))
        if (val.GetDescription<T>() == enumerationDescription)
            return (T)val;

    throw new ArgumentException("ToEnumValue<T>(): Invalid description for enum " + type.Name, "enumerationDescription");
}

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

8
ขอบคุณสำหรับคำติชมเชิงบวก มันยากเสมอที่จะใหม่กับไซต์และเรียนรู้เกี่ยวกับวัฒนธรรมและความแตกต่าง ฉันดีใจที่มีคนเหมือนคุณที่ตั้งคนใหม่ให้ตรง ขอขอบคุณอีกครั้งที่ไม่ได้ทิ้งคนใหม่
Brian Richardson

6
@Jesse และ 4 ปีต่อมามีคนยินดีที่จะหารหัส bjrichardson ที่นี่! ดังนั้นอาจเป็นไซต์ถามตอบ แต่ไม่ได้หมายความว่าคำถามจะตายหลังจากได้รับคำตอบ
จอห์น

18

ทางออกที่ง่ายที่สุดที่นี่คือการใช้วิธีการขยายที่กำหนดเอง (ใน. NET 3.5 อย่างน้อย - คุณสามารถแปลงเป็นวิธีผู้ช่วยคงที่สำหรับรุ่นกรอบก่อนหน้า)

public static string ToCustomString(this PublishStatusses value)
{
    switch(value)
    {
        // Return string depending on value.
    }
    return null;
}

ฉันสมมุติว่าคุณต้องการส่งคืนสิ่งอื่นนอกเหนือจากชื่อจริงของค่า enum (ซึ่งคุณสามารถรับได้โดยเพียงแค่โทร ToString)


แม้ว่าจะถูกต้อง แต่ฉันชอบคุณลักษณะที่มากกว่านี้ วิธีการที่ผมจะสามารถนำวิธีการ toString ของฉันในห้องสมุดแยกต่างหากในขณะที่การวางแสดงสตริงที่กำหนดเองกับ enum ตัวเอง
บอริส Callens

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

1
ใช่มันทั้งหมดขึ้นอยู่กับขอบเขตของวิธีการที่ฉันเดา ในขณะที่วิธีการแอตทริบิวต์เป็นทั่วไปมากขึ้นวิธีการแก้ปัญหาของคุณเป็นภาษาท้องถิ่นมากขึ้น .. มันคือทั้งหมดที่เกี่ยวกับความต้องการในที่สุด
Boris Callens

1
คุณสามารถใส่วิธีการขยายได้ทุกที่ที่คุณต้องการ คุณเพียงแค่ต้องอ้างอิงที่คุณต้องการใช้พวกเขา

ใช่ แต่นี่หมายความว่าวิธีการขยายนี้ควรเขียนใหม่ทุกครั้งที่คุณแนะนำ enum ใหม่ที่คุณต้องการให้มีชื่อเรียกง่าย นอกจากนี้ยังจะหมายความว่าทุกการใช้งานของคุณจะนำชื่อไปรอบ ๆ ที่เป็นมิตรสำหรับทุกการใช้งานอื่น ๆ ของคุณ ...
บอริส Callens

13

โพสต์อื่นนั่นคือ Java คุณไม่สามารถใส่วิธีใน Enums ใน C #

ทำอะไรแบบนี้:

PublishStatusses status = ...
String s = status.ToString();

หากคุณต้องการใช้ค่าการแสดงผลที่แตกต่างกันสำหรับค่า enum ของคุณคุณสามารถใช้แอตทริบิวต์และ Reflection


3
toString ไม่ปลอดภัยในทุกกรณี - enum ที่มีหลายรายการที่มีค่าเดียวกัน (พูดสำหรับจำนวนเต็มจำนวนเต็ม) จะส่งคืนคีย์ของค่าการจับคู่แรกไม่ใช่คีย์ของรายการที่ทดสอบนี่คือเหตุผลที่ต้องการ Enum.GetName
annakata

4
มันเป็นวิธีแก้ปัญหาที่ง่ายที่สุดสำหรับ enum เฉพาะของเขา
เลมมี่

9

วิธีที่ง่ายที่สุดคือการรวมคลาสส่วนขยายนี้ไว้ในโครงการของคุณซึ่งจะทำงานร่วมกับ enum ใด ๆ ในโครงการ:

public static class EnumExtensions
{
    public static string ToFriendlyString(this Enum code)
    {
        return Enum.GetName(code.GetType(), code);
    }
}

การใช้งาน:

enum ExampleEnum
{
    Demo = 0,
    Test = 1, 
    Live = 2
}

...

ExampleEnum ee = ExampleEnum.Live;
Console.WriteLine(ee.ToFriendlyString());

2
มันเป็นเรื่องลึกลับว่าทำไมความคิดเห็นนี้ไม่ได้เป็นที่ยอมรับหรืออัปเดตมากที่สุด - ไม่มีการสะท้อนไม่มีคุณลักษณะที่ไม่จำเป็นเหมาะสำหรับสถานการณ์ง่าย ๆ ที่ enum มีชื่ออยู่แล้ว คุณสามารถใช้คำตอบนี้อีกขั้นตอนหนึ่งและอนุญาตให้มีการเพิ่มช่องว่างระหว่างตัวอักษรใหญ่ก่อนที่จะกลับมา 'My Enum'
Vix

12
หาก enum มีชื่ออยู่แล้วไม่มีความจำเป็นต้องใช้วิธีการขยายใด ๆ เพียงใช้เมธอด ToString () ที่มีอยู่ string result = "Result: " + ee;
จอห์น

นี่ควรเป็นคำตอบที่ดีที่สุด มันทำงานได้สำหรับ Enum ใด ๆ คุณสามารถนำไปใช้งานได้โดยใช้ Enum เฉพาะโดยเปลี่ยนประเภท Enum ของพารามิเตอร์เป็น Enum จริงที่จะใช้
Juanu Haedo

6
คำตอบนี้และความคิดเห็นทั้งหมดไม่สนใจคำขอต้นฉบับสำหรับคำอธิบายเพิ่มเติม พวกคุณพลาดการออกกำลังกายไปอย่างสิ้นเชิงซึ่งจะคืนสิ่งอื่นที่ไม่ใช่ค่า ToString ที่เป็นค่าเริ่มต้น ฉันจะไม่ลงคะแนนหมายเหตุทั้งหมดสำหรับคำตอบนี้ที่นี่ แต่ฉันต้องการ
TonyG

8

ตัวเลือกดั้งเดิมอื่น ๆ เพิ่มเติมที่หลีกเลี่ยงคลาส / ประเภทการอ้างอิง:

  • วิธีการอาร์เรย์
  • วิธีซ้อนโครงสร้าง

วิธีการอาร์เรย์

private struct PublishStatusses
{
    public static string[] Desc = {
        "Not Completed",
        "Completed",
        "Error"
    };

    public enum Id
    {
        NotCompleted = 0,
        Completed,
        Error
    };
}

การใช้

string desc = PublishStatusses.Desc[(int)PublishStatusses.Id.Completed];

วิธีซ้อนโครงสร้าง

private struct PublishStatusses
{
    public struct NotCompleted
    {
        public const int Id = 0;
        public const string Desc = "Not Completed";
    }

    public struct Completed
    {
        public const int Id = 1;
        public const string Desc = "Completed";
    }

    public struct Error
    {
        public const int Id = 2;
        public const string Desc = "Error";
    }            
}

การใช้

int id = PublishStatusses.NotCompleted.Id;
string desc = PublishStatusses.NotCompleted.Desc;

อัปเดต (03/09/2018)

ไฮบริดของวิธีการขยายและเทคนิคแรกข้างต้น

ฉันต้องการให้กำหนดจำนวน enums ที่พวกเขา "เป็น" (ใกล้กับแหล่งกำเนิดของพวกเขาและไม่ได้อยู่ในบาง namespace ทั่วโลกร่วมกัน)

namespace ViewModels
{
    public class RecordVM
    {
        //public enum Enum { Minutes, Hours }
        public struct Enum
        {
            public enum Id { Minutes, Hours }
            public static string[] Name = { "Minute(s)", "Hour(s)" };
        }
    }
}

ดูเหมือนว่าวิธีการขยายเหมาะสำหรับพื้นที่ส่วนกลางและนิยาม "การแปล" ของ enum ตอนนี้ทำให้วิธีการขยายอย่างละเอียดยิ่งขึ้น

namespace Common
{
    public static class EnumExtensions
    {
        public static string Name(this RecordVM.Enum.Id id)
        {
            return RecordVM.Enum.Name[(int)id];
        }
    }   
}

ตัวอย่างการใช้ enum และวิธีการขยาย

namespace Views
{
    public class RecordView 
    {
        private RecordDataFieldList<string, string> _fieldUnit;

        public RecordView()
        {
            _fieldUnit.List = new IdValueList<string, string>
            {            
                new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),
                new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())
            };
        }

        private void Update()
        {    
            RecordVM.Enum.Id eId = DetermineUnit();

            _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;
        }
    }
}

หมายเหตุ:ฉันตัดสินใจที่จะกำจัดEnumwrapper (และNamearray) จริง ๆ แล้วเนื่องจากเป็นสิ่งที่ดีที่สุดที่สตริงชื่อมาจากทรัพยากร (เช่นไฟล์ config หรือ DB) แทนที่จะเป็นรหัสยากและเพราะฉันสิ้นสุดการวางวิธีการขยายในViewModelsเนมสเปซ (ในไฟล์ "CommonVM.cs" อื่น) รวมทุกอย่าง.Idกลายเป็นสิ่งที่ทำให้เสียสมาธิและยุ่งยาก

namespace ViewModels
{
    public class RecordVM
    {
        public enum Enum { Minutes, Hours }
        //public struct Enum
        //{
        //    public enum Id { Minutes, Hours }
        //    public static string[] Name = { "Minute(s)", "Hour(s)" };
        //}
    }
}

CommonVM.cs

//namespace Common
namespace ViewModels
{
    public static class EnumExtensions
    {
        public static string Name(this RecordVM.Enum id)
        {
            //return RecordVM.Enum.Name[(int)id];
            switch (id)
            {
                case RecordVM.Enum.Minutes: return "Minute(s)";                    
                case RecordVM.Enum.Hours: return "Hour(s)";
                default: return null;
            }
        }
    }   
}

ตัวอย่างการใช้ enum และวิธีการขยาย

namespace Views
{
    public class RecordView 
    {
        private RecordDataFieldList<string, string> _fieldUnit

        public RecordView()
        {
            _fieldUnit.List = new IdValueList<string, string>
            {            
                new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),
                new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())
            };
        }

        private void Update()
        {    
            RecordVM.Enum eId = DetermineUnit();

            _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;
        }
    }
}

+ 1-1 = 0 โหวต: โซลูชันนี้สงวนไวยากรณ์ของ Enum และแก้ไขปัญหาได้อย่างสวยงามโดยไม่มีการสะท้อนกลับหรือรหัสที่ซับซ้อนดังนั้น +1 ที่นั่น แต่มันเสียคุณสมบัติของ Enums เอง ดังนั้นแม้ว่า IMO จะเป็นตัวเลือกที่ดี แต่ก็ไม่ตอบคำถามจริงและรับ -1 สุทธิ 0 ขออภัยเราไม่มีวิธีบันทึกที่ดีกว่าใน SO
TonyG

@TonyG ยุติธรรมเพียงพอ หลังจากขาดคำถามสองสามข้อไปกับการประเมินทักษะ. net ของ pluarlsight.com ฉันเริ่มรู้ว่าในเชิงลึกของ C # enum เป็นอย่างไรดังนั้นอาจเป็นความคิดที่ดีที่จะรู้ถึงความสามารถของพวกเขาก่อนที่จะตัดสินใจว่าจะใช้วิธีการใด อาจใช้เวลาสักครู่;)
samis

7

คุณสามารถใช้Humanizerแพคเกจที่มีเมตตากรุณา Enums possiblity ตัวอย่าง:

enum PublishStatusses
{
    [Description("Custom description")]
    NotCompleted,
    AlmostCompleted,
    Error
};

จากนั้นคุณสามารถใช้Humanizeวิธีการขยายบน enum โดยตรง:

var st1 = PublishStatusses.NotCompleted;
var str1 = st1.Humanize(); // will result in Custom description

var st2 = PublishStatusses.AlmostCompleted;
var str2 = st2.Humanize(); // will result in Almost completed (calculated automaticaly)

มันใช้การสะท้อนเช่นกันและไม่ถูกแคช github.com/Humanizr/Humanizer/blob/…
Konrad

มันจะช้าเท่ากับโซลูชันในคำตอบแรกโดยเรย์
คอนราด

5

ด้วยความเคารพ Ray Booysen มีข้อผิดพลาดในรหัส: Enum ToString กับสตริงที่เป็นมิตรต่อผู้ใช้

คุณจำเป็นต้องมีคุณสมบัติหลายบัญชีในค่า enum

public static string GetDescription<T>(this object enumerationValue)
            where T : struct
    {
        Type type = enumerationValue.GetType();
        if (!type.IsEnum)
        {
            throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
        }

        //Tries to find a DescriptionAttribute for a potential friendly name
        //for the enum
        MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
        if (memberInfo != null && memberInfo.Length > 0)
        {
            object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)
            {
                //Pull out the description value
                return ((DescriptionAttribute)attrs.Where(t=>t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description;
            }
        }
        //If we have no description attribute, just return the ToString of the enum
        return enumerationValue.ToString();

4
การละเว้นการตรวจสอบสำหรับคุณลักษณะคำอธิบายหลายรายการนั้นมีจุดประสงค์ หาก enum มีสองแบบและคุณใช้เพื่อสร้างคำอธิบายฉันอยากจะคิดว่านั่นเป็นสถานการณ์ที่พิเศษ ฉันคิดว่าข้อผิดพลาดที่แท้จริงคือฉันไม่ได้ทำ Single () เพื่อให้มีข้อผิดพลาดเกิดขึ้น มิฉะนั้นลายเซ็นของวิธีการทั้งหมดไม่สมเหตุสมผล getDescription ()? คำอธิบายใด สรุปรวม?
Ray Booysen

4
public enum MyEnum
{
    [Description("Option One")]
    Option_One
}

public static string ToDescriptionString(this Enum This)
{
    Type type = This.GetType();

    string name = Enum.GetName(type, This);

    MemberInfo member = type.GetMembers()
        .Where(w => w.Name == name)
        .FirstOrDefault();

    DescriptionAttribute attribute = member != null
        ? member.GetCustomAttributes(true)
            .Where(w => w.GetType() == typeof(DescriptionAttribute))
            .FirstOrDefault() as DescriptionAttribute
        : null;

    return attribute != null ? attribute.Description : name;
}

3
มันดีเสมอที่จะเขียนข้อความอธิบายว่าทำไมสิ่งนี้ถึงใช้ได้และทำไม OP ถึงไม่ได้
phaberest

เพียง FYI ข้อกำหนดรหัส C # ต้องการตัวแปรท้องถิ่นและพารามิเตอร์วิธีที่มีอักษรตัวพิมพ์เล็ก หนึ่งข้อยกเว้นคือthisพารามิเตอร์ในวิธีการส่วนขยายที่คุณสามารถดูได้ว่าThisมีตัวอย่างมากมายในเว็บ การเรียกมันตามประเภทของมันอย่างที่คุณทำ ( Enum Enum) ทำให้โค้ดอ่านน้อยลง
Massimiliano Kraus

4

แทนที่จะใช้ enum ให้ใช้คลาสแบบคงที่

แทนที่

private enum PublishStatuses{
    NotCompleted,
    Completed,
    Error
};

กับ

private static class PublishStatuses{
    public static readonly string NotCompleted = "Not Completed";
    public static readonly string Completed = "Completed";
    public static readonly string Error = "Error";
};

มันจะถูกใช้เช่นนี้

PublishStatuses.NotCompleted; // "Not Completed"

ปัญหาโดยใช้วิธีแก้ปัญหา "วิธีการขยาย" ด้านบน:

Enum ส่วนตัวมักจะใช้ในชั้นเรียนอื่น โซลูชันเมธอดส่วนขยายไม่ถูกต้องเนื่องจากต้องอยู่ในคลาสของตัวเอง โซลูชันนี้สามารถเป็นส่วนตัวและฝังตัวในคลาสอื่น


2

ฉันเป็นแฟน VB.NET ดังนั้นนี่คือรุ่นของฉันซึ่งรวมเมธอด DescriptionAttribute เข้ากับวิธีการขยาย ก่อนผลลัพธ์:

Imports System.ComponentModel ' For <Description>

Module Module1
  ''' <summary>
  ''' An Enum type with three values and descriptions
  ''' </summary>
  Public Enum EnumType
    <Description("One")>
    V1 = 1

    ' This one has no description
    V2 = 2

    <Description("Three")>
    V3 = 3
  End Enum

  Sub Main()
    ' Description method is an extension in EnumExtensions
    For Each v As EnumType In [Enum].GetValues(GetType(EnumType))
      Console.WriteLine("Enum {0} has value {1} and description {2}",
        v,
        CInt(v),
        v.Description
      )
    Next
    ' Output:
    ' Enum V1 has value 1 and description One
    ' Enum V2 has value 2 and description V2
    ' Enum V3 has value 3 and description Three
  End Sub
End Module

สิ่งของพื้นฐาน: Enum เรียกว่า EnumType ที่มีสามค่า V1, V2 และ V3 "วิเศษ" ที่เกิดขึ้นในการเรียก Console.WriteLine ใน Sub Main () v.Descriptionที่อาร์กิวเมนต์สุดท้ายเป็นเพียง สิ่งนี้จะคืนค่า "หนึ่ง" สำหรับ V1, "V2" สำหรับ V2 และ "สาม" สำหรับ V3 คำอธิบาย - วิธีการนี้เป็นวิธีการขยายที่กำหนดไว้ในโมดูลอื่นที่เรียกว่า EnumExtensions:

Option Strict On
Option Explicit On
Option Infer Off

Imports System.Runtime.CompilerServices
Imports System.Reflection
Imports System.ComponentModel

Module EnumExtensions
  Private _Descriptions As New Dictionary(Of String, String)

  ''' <summary>
  ''' This extension method adds a Description method
  ''' to all enum members. The result of the method is the
  ''' value of the Description attribute if present, else
  ''' the normal ToString() representation of the enum value.
  ''' </summary>
  <Extension>
  Public Function Description(e As [Enum]) As String
    ' Get the type of the enum
    Dim enumType As Type = e.GetType()
    ' Get the name of the enum value
    Dim name As String = e.ToString()

    ' Construct a full name for this enum value
    Dim fullName As String = enumType.FullName + "." + name

    ' See if we have looked it up earlier
    Dim enumDescription As String = Nothing
    If _Descriptions.TryGetValue(fullName, enumDescription) Then
      ' Yes we have - return previous value
      Return enumDescription
    End If

    ' Find the value of the Description attribute on this enum value
    Dim members As MemberInfo() = enumType.GetMember(name)
    If members IsNot Nothing AndAlso members.Length > 0 Then
      Dim descriptions() As Object = members(0).GetCustomAttributes(GetType(DescriptionAttribute), False)
      If descriptions IsNot Nothing AndAlso descriptions.Length > 0 Then
        ' Set name to description found
        name = DirectCast(descriptions(0), DescriptionAttribute).Description
      End If
    End If

    ' Save the name in the dictionary:
    _Descriptions.Add(fullName, name)

    ' Return the name
    Return name
  End Function
End Module

เนื่องจากการค้นหาแอตทริบิวต์คำอธิบายโดยใช้Reflectionช้าการค้นหาจึงถูกแคชในส่วนตัวDictionaryด้วยซึ่งจะถูกเติมตามความต้องการ

(ขออภัยสำหรับโซลูชัน VB.NET - ควรจะค่อนข้างตรงไปตรงมาเพื่อแปลเป็น C # และ C # ของฉันเป็นสนิมในวิชาใหม่เช่นส่วนขยาย)


2

สรุปการทำความสะอาดของคำแนะนำข้างต้นพร้อมตัวอย่าง:

namespace EnumExtensions {

using System;
using System.Reflection;

public class TextAttribute : Attribute {
   public string Text;
   public TextAttribute( string text ) {
      Text = text;
   }//ctor
}// class TextAttribute

public static class EnumExtender {

public static string ToText( this Enum enumeration ) {

   MemberInfo[] memberInfo = enumeration.GetType().GetMember( enumeration.ToString() );

   if ( memberInfo != null && memberInfo.Length > 0 ) {

      object[] attributes = memberInfo[ 0 ].GetCustomAttributes( typeof(TextAttribute),  false );

      if ( attributes != null && attributes.Length > 0 ) {
         return ( (TextAttribute)attributes[ 0 ] ).Text;
      }

   }//if

   return enumeration.ToString();

}//ToText

}//class EnumExtender

}//namespace

การใช้:

using System;
using EnumExtensions;

class Program {

public enum Appearance {

  [Text( "left-handed" ) ]
  Left,

  [Text( "right-handed" ) ]
  Right,

}//enum

static void Main( string[] args ) {

   var appearance = Appearance.Left;
   Console.WriteLine( appearance.ToText() );

}//Main

}//class

1
มีแอตทริบิวต์ [คำอธิบาย ("")] ใน C # ทำไมไม่ใช้สิ่งนี้
Stefan Koenen

แน่นอนว่าการใช้ [คำอธิบาย ("")] เป็นวิธีที่จะไป แต่ฉันต้องการตัวอย่างให้สมบูรณ์
ขีดล่าง

2

ใช้Enum.GetName

จากลิงค์ด้านบน ...

using System;

public class GetNameTest {
    enum Colors { Red, Green, Blue, Yellow };
    enum Styles { Plaid, Striped, Tartan, Corduroy };

    public static void Main() {

        Console.WriteLine("The 4th value of the Colors Enum is {0}", Enum.GetName(typeof(Colors), 3));
        Console.WriteLine("The 4th value of the Styles Enum is {0}", Enum.GetName(typeof(Styles), 3));
    }
}
// The example displays the following output:
//       The 4th value of the Colors Enum is Yellow
//       The 4th value of the Styles Enum is Corduroy

2

ตามเอกสารนี้: https://docs.microsoft.com/pt-br/dotnet/api/system.enum.tostring?view=netframework-4.8

เป็นไปได้ที่จะแปลงตัวแจงนับเป็นสตริงโดยใช้รูปแบบดังนี้:

public enum Example
{
    Example1,
    Example2
}

Console.WriteLine(Example.Example1.ToString("g"));

//Outputs: "Example1"

คุณสามารถดูรูปแบบที่เป็นไปได้ทั้งหมดในลิงค์นี้: https://docs.microsoft.com/pt-br/dotnet/api/system.string?view=netframework-4.8


1

นี่คือการอัปเดตโค้ดของ Ray Booysen ที่ใช้เมธอด GetCustomAttributes ทั่วไปและ LINQ เพื่อทำให้สิ่งต่าง ๆ เป็นระเบียบมากขึ้น

    /// <summary>
    /// Gets the value of the <see cref="T:System.ComponentModel.DescriptionAttribute"/> on an struct, including enums.  
    /// </summary>
    /// <typeparam name="T">The type of the struct.</typeparam>
    /// <param name="enumerationValue">A value of type <see cref="T:System.Enum"/></param>
    /// <returns>If the struct has a Description attribute, this method returns the description.  Otherwise it just calls ToString() on the struct.</returns>
    /// <remarks>Based on http://stackoverflow.com/questions/479410/enum-tostring/479417#479417, but useful for any struct.</remarks>
    public static string GetDescription<T>(this T enumerationValue) where T : struct
    {
        return enumerationValue.GetType().GetMember(enumerationValue.ToString())
                .SelectMany(mi => mi.GetCustomAttributes<DescriptionAttribute>(false),
                    (mi, ca) => ca.Description)
                .FirstOrDefault() ?? enumerationValue.ToString();
    }   

ล้มเหลวในการดูว่าทำไมคุณต้องเป็นแบบทั่วไป? ถ้าคุณจะใช้การสะท้อน?
Lee Louviere

@LeeLouviere เป็นหลักในการหลีกเลี่ยงการชกมวยเมื่อผ่านโครงสร้าง (ประเภทค่า) เป็นพารามิเตอร์
Richard Anthony Hein

1
แทน numerationValue.GetType () ให้ใช้: typeof (T)
สลาวา

1
การปรับปรุงบรรทัดเดียวขนาดใหญ่เหนือคำตอบที่ยอมรับโดยไม่ต้อง (YMMV) สูญเสียความสามารถในการอ่าน ใช่ด้วย typeof (T)
TonyG

1

สรุปได้ดียิ่งขึ้น:

using System;
using System.Reflection;

public class TextAttribute : Attribute
{
    public string Text;
    public TextAttribute(string text)
    {
        Text = text;
    }
}  

public static class EnumExtender
{
    public static string ToText(this Enum enumeration)
    {
        var memberInfo = enumeration.GetType().GetMember(enumeration.ToString());
        if (memberInfo.Length <= 0) return enumeration.ToString();

        var attributes = memberInfo[0].GetCustomAttributes(typeof(TextAttribute), false);
        return attributes.Length > 0 ? ((TextAttribute)attributes[0]).Text : enumeration.ToString();
    }
}

การใช้งานเช่นเดียวกับขีดเส้นใต้อธิบาย


0

สำหรับธง enum ได้แก่

    public static string Description(this Enum value)
    {
        Type type = value.GetType();

        List<string> res = new List<string>();
        var arrValue = value.ToString().Split(',').Select(v=>v.Trim());
        foreach (string strValue in arrValue)
        {
            MemberInfo[] memberInfo = type.GetMember(strValue);
            if (memberInfo != null && memberInfo.Length > 0)
            {
                object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)
                {
                    res.Add(((DescriptionAttribute)attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description);
                }
                else
                    res.Add(strValue);
            }
            else
                res.Add(strValue);
        }

        return res.Aggregate((s,v)=>s+", "+v);
    }

0

ในกรณีที่คุณต้องการเพิ่มช่องว่างระหว่างคำมันง่ายเหมือน

string res = Regex.Replace(PublishStatusses.NotCompleted, "[A-Z]", " $0").Trim();

0

ฉันใช้คลาสทั่วไปเพื่อเก็บคู่ enum / description และคลาสตัวช่วยเหลือแบบซ้อนเพื่อรับคำอธิบาย

enum :

enum Status { Success, Fail, Pending }

ชั้นสามัญ:

หมายเหตุ:เนื่องจากคลาสทั่วไปไม่สามารถถูก จำกัด โดยenumฉันถูกบังคับโดยstructแทนและตรวจสอบenumใน Constructor

public class EnumX<T> where T : struct
{
    public T Code { get; set; }
    public string Description { get; set; }

    public EnumX(T code, string desc)
    {
        if (!typeof(T).IsEnum) throw new NotImplementedException();

        Code = code;
        Description = desc;
    }

    public class Helper
    {
        private List<EnumX<T>> codes;

        public Helper(List<EnumX<T>> codes)
        {
            this.codes = codes;
        }

        public string GetDescription(T code)
        {
            EnumX<T> e = codes.Where(c => c.Code.Equals(code)).FirstOrDefault();
            return e is null ? "Undefined" : e.Description;
        }
    }
}

การใช้งาน:

EnumX<Status>.Helper StatusCodes = new EnumX<Status>.Helper(new List<EnumX<Status>>()
        {
            new EnumX<Status>(Status.Success,"Operation was successful"),
            new EnumX<Status>(Status.Fail,"Operation failed"),
            new EnumX<Status>(Status.Pending,"Operation not complete. Please wait...")
        });

        Console.WriteLine(StatusCodes.GetDescription(Status.Pending));

-2

ฉันคิดว่าวิธีที่ดีที่สุด (และง่ายที่สุด) ในการแก้ปัญหาของคุณคือการเขียน Extension-Method สำหรับ enum ของคุณ:

public static string GetUserFriendlyString(this PublishStatusses status)
    {

    }

1
มีคนก่อนหน้านี้ 7 ปีที่ระบุว่า
Steven

-3

หากคุณต้องการบางสิ่งบางอย่างที่ปรับแต่งได้อย่างสมบูรณ์ลองโซลูชันของฉันที่นี่:

http://www.kevinwilliampang.com/post/Mapping-Enums-To-Strings-and-Strings-to-Enums-in-NET.aspx

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


1
และตอนนี้เว็บไซต์ไม่ทำงาน
Nic Foster
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.