سؤال

يتكون التعداد الخاص بي من القيم التالية:

private enum PublishStatusses{
    NotCompleted,
    Completed,
    Error
};

أريد أن أكون قادرًا على إخراج هذه القيم بطريقة سهلة الاستخدام.
لا أحتاج إلى أن أكون قادرًا على الانتقال من السلسلة إلى القيمة مرة أخرى.

هل كانت مفيدة؟

المحلول

وأنا استخدم Description سمة من النظام .ComponentModel مساحة الاسم. ببساطة تزيين التعداد:

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

نصائح أخرى

أفعل ذلك باستخدام طرق التمديد:

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

وربما أنا شيء مفقود، ولكن ما هو الخطأ في Enum.GetName؟

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

وتحرير: سلاسل سهل الاستعمال، عليك أن تذهب من خلال .resource للحصول على تدويل / توطين القيام به، وسيكون من القول الأفضل استخدام مفتاح ثابتة على أساس مفتاح التعداد من سمة الديكور على نفسه.

وأنا خلقت طريقة تمديد العكسي لتحويل وصف يعود إلى قيمة التعداد:

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

وأسهل حل هنا هو استخدام طريقة تمديد حسب الطلب (في. NET لا يقل عن 3.5 - يمكنك فقط تحويله إلى أسلوب مساعد ثابت للإصدارات السابقة الإطار).

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

وانا افترض هنا ان كنت ترغب في العودة شيء آخر غير الاسم الفعلي من قيمة التعداد (والتي يمكنك الحصول عليها عن طريق الاتصال بالرقم ToString).

وهذا وظيفة أخرى هي جافا. لا يمكنك وضع الأساليب في تتضمن التعدادات في C #.

ومجرد القيام بشيء من هذا القبيل:

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

إذا كنت تريد استخدام قيم عرض مختلفة للقيم التعداد الخاص بك، يمكنك استخدام سمات والتأمل.

وأبسط طريقة هي فقط لتشمل هذه الفئة التمديد في المشروع، وأنها ستعمل مع أي التعداد في المشروع:

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

بعض الخيارات البدائية الأخرى التي تتجنب الفئات/أنواع المراجع:

  • طريقة المصفوفة
  • طريقة البناء المتداخلة

طريقة المصفوفة

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)

مزيج من طرق التمديد والتقنية الأولى أعلاه.

أفضل أن يتم تعريف التعدادات في المكان الذي "تنتمي إليه" (الأقرب إلى مصدرها الأصلي وليس في مساحة اسم عالمية مشتركة).

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

تبدو طريقة الامتداد مناسبة لمنطقة مشتركة، والتعريف "المحلي" للتعداد يجعل الآن طريقة الامتداد أكثر تفصيلاً.

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

مثال لاستخدام التعداد وطريقة تمديده.

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

ملحوظة: والحقيقة أنني قررت التخلص من Enum غلاف (و Name array)، نظرًا لأنه من الأفضل أن تأتي سلاسل الأسماء من مورد (أي ملف التكوين أو قاعدة البيانات) بدلاً من أن تكون مشفرة، ولأنني انتهيت من وضع طريقة الامتداد في 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;
            }
        }
    }   
}

مثال لاستخدام التعداد وطريقة تمديده.

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

ويمكنك استخدام حزمة Humanizer مع <لأ href = "https://github.com/Humanizr / Humanizer # انسنة-تتضمن التعدادات "يختلط =" noreferrer "> أنسنة تتضمن التعدادات امكانية. وeaxample:

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

وبعد ذلك يمكنك استخدام طريقة تمديد Humanize على التعداد مباشرة:

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)

فيما يتعلق بـ Ray Booysen، هناك خطأ في الكود: Enum ToString مع سلاسل سهلة الاستخدام

تحتاج إلى حساب سمات متعددة في قيم التعداد.

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

وبدلا من استخدام التعداد استخدام فئة ثابتة.

واستبدال

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"

العدد باستخدام أعلى حلول "طريقة التمديد":

وغالبا ما يتم استخدام التعداد الخاص داخل فئة أخرى. الحل طريقة التمديد هو غير صالح هناك لأنه يجب أن يكون في انها من الدرجة الخاصة. هذا الحل يمكن أن يكون الخاص وجزءا لا يتجزأ من فئة أخرى.

وأنا يحدث ليكون مروحة 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

والاشياء الأساسي: وهو التعداد دعا EnumType مع ثلاث قيم V1، V2 و V3. "سحر" يحدث في الدعوة Console.WriteLine في Sub الرئيسي ()، حيث الحجة الأخيرة هي ببساطة 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 - ينبغي أن يكون straighforward نسبيا ترجمتها إلى C #، وبلدي C # هو صدئ على المواضيع الجديدة مثل التمديدات)

وملخص تنظيف الاقتراحات المذكورة أعلاه مع عينة:

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

وهذا هو التحديث على شفرة راي بويسون أن يستخدم الأسلوب 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();
    }   

وملخص حتى أنظف:

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

ونفس الاستخدام كما تسطير يصف.

يستخدم 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

لأعلام التعداد بما في ذلك.

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

وأعتقد أن أفضل (وأسهل) وسيلة لحل مشكلتك هي كتابة ملحق-طريقة لالتعداد الخاص بك:

public static string GetUserFriendlyString(this PublishStatusses status)
    {

    }

إذا كنت تريد شيئا للتخصيص تماما، تجربة بلدي الحل هنا:

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

والأساس، وآخر وتحدد كيفية إرفاق الوصف سمات لكل من تتضمن التعدادات ويوفر وسيلة عامة لتعيين من التعداد إلى الوصف.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top