[c#] تمثيل سلسلة التعداد


14 Answers

طريقة الاستخدام

Enum.GetName(Type MyEnumType,  object enumvariable)  

كما في (افتراض Shipper هو التعداد المحدد)

Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);

هناك مجموعة من الأساليب الثابتة الأخرى على صف التعداد تستحق التحقيق أيضًا ...

Question

لدي التعداد التالي:

public enum AuthenticationMethod
{
    FORMS = 1,
    WINDOWSAUTHENTICATION = 2,
    SINGLESIGNON = 3
}

ولكن المشكلة هي أنني أحتاج إلى كلمة "FORMS" عندما أطلب AuthenticationMethod.FORMS وليس المعرّف 1.

لقد وجدت الحل التالي لهذه المشكلة ( link ):

أولاً ، أحتاج إلى إنشاء سمة مخصصة تسمى "StringValue":

public class StringValue : System.Attribute
{
    private readonly string _value;

    public StringValue(string value)
    {
        _value = value;
    }

    public string Value
    {
        get { return _value; }
    }

}

ثم يمكنني إضافة هذه السمة إلى العداد الخاص بي:

public enum AuthenticationMethod
{
    [StringValue("FORMS")]
    FORMS = 1,
    [StringValue("WINDOWS")]
    WINDOWSAUTHENTICATION = 2,
    [StringValue("SSO")]
    SINGLESIGNON = 3
}

وبالطبع أحتاج إلى شيء لاسترداد ذلك StringValue:

public static class StringEnum
{
    public static string GetStringValue(Enum value)
    {
        string output = null;
        Type type = value.GetType();

        //Check first in our cached results...

        //Look for our 'StringValueAttribute' 

        //in the field's custom attributes

        FieldInfo fi = type.GetField(value.ToString());
        StringValue[] attrs =
           fi.GetCustomAttributes(typeof(StringValue),
                                   false) as StringValue[];
        if (attrs.Length > 0)
        {
            output = attrs[0].Value;
        }

        return output;
    }
}

جيد الآن لقد حصلت على الأدوات للحصول على قيمة سلسلة لعداد. يمكنني بعد ذلك استخدامه مثل هذا:

string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);

حسناً ، كل هذه الأشياء تعمل مثل السحر ، لكني أجد الكثير من العمل. كنت أتساءل إذا كان هناك حل أفضل لهذا.

كما أنني حاولت شيئا مع قاموس وخصائص ثابتة ولكن ذلك لم يكن أفضل سواء.




حل بسيط جدا لهذا مع. صافي 4.0 وما فوق. لا يوجد رمز آخر مطلوب.

public enum MyStatus
{
    Active = 1,
    Archived = 2
}

للحصول على السلسلة حول الاستخدام فقط:

MyStatus.Active.ToString("f");

أو

MyStatus.Archived.ToString("f");`

ستكون القيمة "نشطة" أو "مؤرشفة".

لمشاهدة تنسيقات السلسلة المختلفة ("f" من أعلى) عند استدعاء Enum.ToString انظر صفحة سلاسل عمليات التعداد هذه




Here is yet another way to accomplish the task of associating strings with enums:

struct DATABASE {
    public enum enums {NOTCONNECTED, CONNECTED, ERROR}
    static List<string> strings =
        new List<string>() {"Not Connected", "Connected", "Error"};

    public string GetString(DATABASE.enums value) {
        return strings[(int)value];
    }
}

This method is called like this:

public FormMain() {
    DATABASE dbEnum;

    string enumName = dbEnum.GetString(DATABASE.enums.NOTCONNECTED);
}

You can group related enums in their own struct. Since this method uses the enum type, you can use Intellisense to display the list of enums when making the GetString() call.

You can optionally use the new operator on the DATABASE struct. Not using it means the strings List is not allocated until the first GetString() call is made.




فقط استخدم الأسلوب ToString()

public enum any{Tomato=0,Melon,Watermelon}

للإشارة إلى سلسلة Tomato ، استخدم فقط

any.Tomato.ToString();



A lot of great answers here but in my case did not solve what I wanted out of an "string enum", which was:

  1. Usable in a switch statement eg switch(myEnum)
  2. Can be used in function parameters eg foo(myEnum type)
  3. Can be referenced eg myEnum.FirstElement
  4. I can use strings eg foo("FirstElement") == foo(myEnum.FirstElement)

1,2 & 4 can actually be solved with a C# Typedef of a string (since strings are switchable in c#)

3 can be solved by static const strings. So if you have the same needs, this is the simplest approach:

public sealed class Types
{

    private readonly String name;

    private Types(String name)
    {
        this.name = name;

    }

    public override String ToString()
    {
        return name;
    }

    public static implicit operator Types(string str)
    {
        return new Types(str);

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }


    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";
    public const string Folder = "Folder";
    #endregion

}

This allows for example:

    public TypeArgs(Types SelectedType)
    {
        Types SelectedType = SelectedType
    }

و

public TypeObject CreateType(Types type)
    {
        switch (type)
        {

            case Types.ImageType:
              //
                break;

            case Types.DataType:
             //
                break;

        }
    }

Where CreateType can be called with a string or a type. However the downside is that any string is automatically a valid enum , this could be modified but then it would require some kind of init function...or possibly make they explicit cast internal?

Now if an int value was important to you (perhaps for comparison speed), you could use some ideas from Jakub Šturc fantastic answer and do something a bit crazy, this is my stab at it:

    public sealed class Types
{
    private static readonly Dictionary<string, Types> strInstance = new Dictionary<string, Types>();
    private static readonly Dictionary<int, Types> intInstance = new Dictionary<int, Types>();

    private readonly String name;
    private static int layerTypeCount = 0;
    private int value;
    private Types(String name)
    {
        this.name = name;
        value = layerTypeCount++;
        strInstance[name] = this;
        intInstance[value] = this;
    }

    public override String ToString()
    {
        return name;
    }


    public static implicit operator Types(int val)
    {
        Types result;
        if (intInstance.TryGetValue(val, out result))
            return result;
        else
            throw new InvalidCastException();
    }

    public static implicit operator Types(string str)
    {
        Types result;
        if (strInstance.TryGetValue(str, out result))
        {
            return result;
        }
        else
        {
            result = new Types(str);
            return result;
        }

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }

    public static bool operator ==(Types a, Types b)
    {
        return a.value == b.value;
    }
    public static bool operator !=(Types a, Types b)
    {
        return a.value != b.value;
    }

    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";

    #endregion

}

but of course "Types bob = 4;" would be meaningless unless you had initialized them first which would sort of defeat the point...

But in theory TypeA == TypeB would be quicker...




أستخدم مزيجًا من العديد من الاقتراحات أعلاه ، مع بعض التخزين المؤقت. الآن ، حصلت على فكرة من بعض التعليمات البرمجية التي وجدت في مكان ما على شبكة الإنترنت ، ولكن لا أستطيع تذكر أين حصلت عليه أو العثور عليه. لذلك إذا وجد أي شخص شيئًا مشابهًا ، فيُرجى التعليق على الإسناد.

على أي حال ، يتضمن الاستخدام محولات النوع ، لذلك إذا كنت ملزماً لـ UI "تعمل فقط". يمكنك تمديد مع نمط Jakub للبحث عن رمز سريع عن طريق تهيئة من نوع المحول إلى أساليب ثابتة.

قد يبدو استخدام القاعدة هكذا

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
    // The custom type converter will use the description attribute
    [Description("A custom description")]
    ValueWithCustomDescription,

   // This will be exposed exactly.
   Exact
}

يتبع رمز محول نوع التعداد المخصص:

public class CustomEnumTypeConverter<T> : EnumConverter
    where T : struct
{
    private static readonly Dictionary<T,string> s_toString = 
      new Dictionary<T, string>();

    private static readonly Dictionary<string, T> s_toValue = 
      new Dictionary<string, T>();

    private static bool s_isInitialized;

    static CustomEnumTypeConverter()
    {
        System.Diagnostics.Debug.Assert(typeof(T).IsEnum,
          "The custom enum class must be used with an enum type.");
    }

    public CustomEnumTypeConverter() : base(typeof(T))
    {
        if (!s_isInitialized)
        {
            Initialize();
            s_isInitialized = true;
        }
    }

    protected void Initialize()
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            string description = GetDescription(item);
            s_toString[item] = description;
            s_toValue[description] = item;
        }
    }

    private static string GetDescription(T optionValue)
    {
        var optionDescription = optionValue.ToString();
        var optionInfo = typeof(T).GetField(optionDescription);
        if (Attribute.IsDefined(optionInfo, typeof(DescriptionAttribute)))
        {
            var attribute = 
              (DescriptionAttribute)Attribute.
                 GetCustomAttribute(optionInfo, typeof(DescriptionAttribute));
            return attribute.Description;
        }
        return optionDescription;
    }

    public override object ConvertTo(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, 
       object value, Type destinationType)
    {
        var optionValue = (T)value;

        if (destinationType == typeof(string) && 
            s_toString.ContainsKey(optionValue))
        {
            return s_toString[optionValue];
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, object value)
    {
        var stringValue = value as string;

        if (!string.IsNullOrEmpty(stringValue) && s_toValue.ContainsKey(stringValue))
        {
            return s_toValue[stringValue];
        }

        return base.ConvertFrom(context, culture, value);
    }
}

}




في سؤالك لم تقل أبداً أنك تحتاج بالفعل إلى القيمة الرقمية للتعداد في أي مكان.

إذا كنت لا تحتاج فقط إلى تعداد سلسلة من النوع (وهو ليس نوعًا لا يتجزأ لا يمكن أن يكون أساسًا للتعداد) فإليك طريقة:

    static class AuthenticationMethod
    {
        public static readonly string
            FORMS = "Forms",
            WINDOWSAUTHENTICATION = "WindowsAuthentication";
    }

يمكنك استخدام نفس بناء التعداد كمرجع

if (bla == AuthenticationMethod.FORMS)

سيكون أبطأ قليلاً من القيم الرقمية (مقارنة السلاسل بدلاً من الأرقام) ولكن على الجانب الإيجابي لا يستخدم الانعكاس (البطيء) للوصول إلى السلسلة.




عندما أواجه هذه المشكلة ، هناك بعض الأسئلة التي أحاول أن أجيب عليها أولاً:

  • هل أسماء قيم التعداد الخاصة بي ملائمة بشكل كافٍ لهذا الغرض ، أم أحتاج إلى توفير قيم أكثر صداقة؟
  • هل أحتاج إلى رحلة ذهاب وعودة؟ أي ، هل سأحتاج إلى أخذ قيم نصية وتحليلها في قيم التعداد؟
  • هل هذا شيء أحتاج إلى القيام به للعديد من التعدادات في مشروعي ، أو واحد فقط؟
  • أي نوع من عناصر واجهة المستخدم سوف أقوم بتقديم هذه المعلومات - على وجه الخصوص ، هل سأكون ملزمة لواجهة المستخدم ، أو باستخدام أوراق الملكية؟
  • هل هذا يحتاج إلى أن يكون قابلاً للتحديث؟

إن أبسط طريقة للقيام بذلك هي Enum.GetValue (ودعم round-tripping باستخدام Enum.Parse ). كما أنه في كثير من الأحيان يستحق بناء TypeConverter ، كما يقترح ستيف ميتشام ، لدعم ربط واجهة المستخدم. (ليس من الضروري إنشاء TypeConverter عند استخدام أوراق الملكية ، وهي واحدة من الأشياء اللطيفة حول أوراق الملكية. على الرغم من أن اللورد يعرف أن لديه TypeConverter الخاصة.)

بشكل عام ، إذا كانت الإجابات على الأسئلة المذكورة أعلاه تشير إلى أن هذا لن ينجح ، فإن الخطوة التالية هي إنشاء Dictionary<MyEnum, string> ثابت Dictionary<MyEnum, string> ، أو Dictionary<MyEnum, string> ، أو ربما Dictionary<Type, Dictionary<int, string>> . أميل إلى تخطي الخطوة المتوسطة لتزيين-رمز-مع-الصفات لأن ما يأتي عادة أسفل البايك القادم هو الحاجة إلى تغيير القيم الودية بعد النشر (في كثير من الأحيان ، ولكن ليس دائماً ، بسبب التعريب).




لسوء الحظ ، فإن التفكير في الحصول على سمات على التعداد بطيء جدًا:

راجع هذا السؤال: أي شخص يعرف طريقة سريعة للوصول إلى سمات مخصصة على قيمة التعداد؟

.ToString() بطيء جدًا على التعداد أيضًا.

يمكنك كتابة طرق الإضافة للتعداد على الرغم من:

public static string GetName( this MyEnum input ) {
    switch ( input ) {
        case MyEnum.WINDOWSAUTHENTICATION:
            return "Windows";
        //and so on
    }
}

هذا ليس رائعًا ، ولكنه سيكون سريعًا ولا يتطلب انعكاسًا للسمات أو اسم الحقل.

تحديث C # 6

إذا كان بإمكانك استخدام C # 6 ، فإن مشغل nameof الجديد يعمل على nameof(MyEnum.WINDOWSAUTHENTICATION) ، لذلك سيتم تحويل nameof(MyEnum.WINDOWSAUTHENTICATION) إلى "WINDOWSAUTHENTICATION" في وقت التحويل البرمجي ، مما يجعله أسرع طريقة للحصول على أسماء التعداد.

لاحظ أن هذا سيحول التعداد الصريح إلى ثابت مضمن ، لذلك لا يعمل مع التعدادات التي لديك في المتغير. وبالتالي:

nameof(AuthenticationMethod.FORMS) == "FORMS"

لكن...

var myMethod = AuthenticationMethod.FORMS;
nameof(myMethod) == "myMethod"



old post but...

The answer to this may actually be very simple. Use Enum.ToString() function

There are 6 overloads of this function, you can use Enum.Tostring("F") or Enum.ToString() to return the string value. No need to bother with anything else. Here is a working Demo

Note that this solution may not work for all compilers ( this demo does not work as expected ) but at least it works for the latest compiler.




My answer, working on @user29964 's answer (which is by far the simplest and closest to a Enum) is

 public class StringValue : System.Attribute
    {
        private string _value;

        public StringValue(string value)
        {
            _value = value;
        }

        public string Value
        {
            get { return _value; }
        }



        public static string GetStringValue(Enum Flagvalue)
        {
            Type type = Flagvalue.GetType();
            string[] flags = Flagvalue.ToString().Split(',').Select(x => x.Trim()).ToArray();
            List<string> values = new List<string>();

            for (int i = 0; i < flags.Length; i++)
            {

                FieldInfo fi = type.GetField(flags[i].ToString());

                StringValue[] attrs =
                   fi.GetCustomAttributes(typeof(StringValue),
                                           false) as StringValue[];
                if (attrs.Length > 0)
                {
                    values.Add(attrs[0].Value);
                }
            }
            return String.Join(",", values);

        }

استعمال

[Flags]
    public enum CompeteMetric
    {

        /// <summary>
        /// u
        /// </summary>
        [StringValue("u")]//Json mapping
        Basic_UniqueVisitors = 1 //Basic
             ,
        /// <summary>
        /// vi
        /// </summary>
        [StringValue("vi")]//json mapping
        Basic_Visits = 2// Basic
            ,
        /// <summary>
        /// rank
        /// </summary>
        [StringValue("rank")]//json mapping
        Basic_Rank = 4//Basic
 }

مثال

        CompeteMetric metrics = CompeteMetric.Basic_Visits | CompeteMetric.Basic_Rank;
        string strmetrics = StringValue.GetStringValue(metrics);

this will return "vi,rank"




أتفق مع كيث ، لكن لا يمكنني التصويت (حتى الآن).

أنا استخدم طريقة ثابتة وبيان سويث للعودة بالضبط ما أريد. في قاعدة البيانات I تخزين tinyint ويستخدم رمز فقط التعداد الفعلي ، بحيث يتم السلاسل لمتطلبات واجهة المستخدم. بعد إجراء العديد من الاختبارات ، أدى ذلك إلى تحقيق أفضل أداء وأفضل تحكم في الإخراج.

public static string ToSimpleString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "ComplexForms";
             break;
     }
}

public static string ToFormattedString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "Complex Forms";
             break;
     }
}

ومع ذلك ، من خلال بعض الحسابات ، وهذا يؤدي إلى كابوس الصيانة المحتملة وبعض رائحة الكود. أحاول أن أبقى عينًا على التعدادات الطويلة والكثير من التعدادات ، أو تلك التي تتغير كثيرًا. خلاف ذلك ، كان هذا حلا رائعا بالنسبة لي.




If you think about the problem we're trying to solve, it's not an enum we need at all. We need an object that allows a certain number of values to be associated with eachother; in other words, to define a class.

Jakub Šturc's type-safe enum pattern is the best option I see here.

Look at it:

  • It has a private constructor so only the class itself can define the allowed values.
  • It is a sealed class so values can't be modifed through inheritence.
  • It is type-safe, allowing your methods to require only that type.
  • There is no reflection performance hit incurred by accessing the values.
  • And lastly, it can be modified to associate more than two fields together, for example a Name, Description, and a numeric Value.



بلدي البديل

public struct Colors
{
    private String current;

    private static string red = "#ff0000";
    private static string green = "#00ff00";
    private static string blue = "#0000ff";

    private static IList<String> possibleColors; 

    public static Colors Red { get { return (Colors) red; } }
    public static Colors Green { get { return (Colors) green; } }
    public static Colors Blue { get { return (Colors) blue; } }

    static Colors()
    {
        possibleColors = new List<string>() {red, green, blue};
    }

    public static explicit operator String(Colors value)
    {
        return value.current;
    }

    public static explicit operator Colors(String value)
    {
        if (!possibleColors.Contains(value))
        {
            throw new InvalidCastException();
        }

        Colors color = new Colors();
        color.current = value;
        return color;
    }

    public static bool operator ==(Colors left, Colors right)
    {
        return left.current == right.current;
    }

    public static bool operator !=(Colors left, Colors right)
    {
        return left.current != right.current;
    }

    public bool Equals(Colors other)
    {
        return Equals(other.current, current);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (obj.GetType() != typeof(Colors)) return false;
        return Equals((Colors)obj);
    }

    public override int GetHashCode()
    {
        return (current != null ? current.GetHashCode() : 0);
    }

    public override string ToString()
    {
        return current;
    }
}

Code looks a bit ugly, but usages of this struct are pretty presentative.

Colors color1 = Colors.Red;
Console.WriteLine(color1); // #ff0000

Colors color2 = (Colors) "#00ff00";
Console.WriteLine(color2); // #00ff00

// Colors color3 = "#0000ff"; // Compilation error
// String color4 = Colors.Red; // Compilation error

Colors color5 = (Colors)"#ff0000";
Console.WriteLine(color1 == color5); // True

Colors color6 = (Colors)"#00ff00";
Console.WriteLine(color1 == color6); // False

Also, I think, if a lot of such enums required, code generation (eg T4) might be used.




Well, after reading all of the above I feel that the guys have over complicated the issue of transforming enumerators into strings. I liked the idea of having attributes over enumerated fields but i think that attributes are mainly used for Meta-data, but in your case i think that all you need is some sort of localization.

public enum Color 
{ Red = 1, Green = 2, Blue = 3}


public static EnumUtils 
{
   public static string GetEnumResourceString(object enumValue)
    {
        Type enumType = enumValue.GetType();
        string value = Enum.GetName(enumValue.GetType(), enumValue);
        string resourceKey = String.Format("{0}_{1}", enumType.Name, value);
        string result = Resources.Enums.ResourceManager.GetString(resourceKey);
        if (string.IsNullOrEmpty(result))
        {
            result = String.Format("{0}", value);
        }
        return result;
    }
}

Now if we try to call the above method we can call it this way

public void Foo()
{
  var col = Color.Red;
  Console.WriteLine (EnumUtils.GetEnumResourceString (col));
}

All you need to do is just create a resource file containing all the enumerator values and the corresponding strings

Resource Name          Resource Value
Color_Red              My String Color in Red
Color_Blue             Blueeey
Color_Green            Hulk Color

What is actually very nice about that is that it will be very helpful if you need your application to be localized, since all you need to do is just create another resource file with your new language! and Voe-la!






Related



Tags

c# c#   enums