c# شرح - تمثيل سلسلة التعداد




15 Answers

جرب نمط type-safe-enum .

public sealed class AuthenticationMethod {

    private readonly String name;
    private readonly int value;

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (3, "SSN");        

    private AuthenticationMethod(int value, String name){
        this.name = name;
        this.value = value;
    }

    public override String ToString(){
        return name;
    }

}

تحديث يمكن إجراء تحويل نوع صريح (أو ضمني) بواسطة

  • إضافة حقل ثابت مع التعيين

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    
    • ملحوظة: من أجل أن لا تؤدي أولوية حقول "عضو التعداد" إلى إلقاء NullReferenceException عند استدعاء مُنشئ المثيل ، تأكد من وضع حقل القاموس قبل حقول "عضو التعداد" في الفصل الدراسي. ويرجع ذلك إلى أنه يتم استدعاء ألقاب الحقل الثابت في ترتيب تعريف ، وقبل منشئ ثابت ، إنشاء الموقف الغريب والضروري لكن المربك الذي يمكن استدعاء مُنشئ المثيل قبل أن تتم تهيئة كافة الحقول الثابتة ، وقبل استدعاء المُنشئ الاستاتيكي.
  • ملء هذا التعيين على سبيل المثال منشئ

    instance[name] = this;
    
  • وإضافة عامل تحويل نوع المعرفة من قبل المستخدم

    public static explicit operator AuthenticationMethod(string str)
    {
        AuthenticationMethod result;
        if (instance.TryGetValue(str, out result))
            return result;
        else
            throw new InvalidCastException();
    }
    
enum combination

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

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

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

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




يمكنك الرجوع إلى الاسم بدلاً من القيمة باستخدام ToString ()

Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());

الوثائق هنا:

http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx

... وإذا قمت بتسمية التعدادات الخاصة بك في Pascal Case (كما أفعل - مثل ThisIsMyEnumValue = 1 إلخ) ، يمكنك استخدام تعبير عادي بسيط لطباعة النموذج المألوف:

static string ToFriendlyCase(this string EnumString)
{
    return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");
}

والتي يمكن استدعاؤها بسهولة من أي سلسلة:

Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());

المخرجات:

تحويل My Crazy Pascal Case Sentence إلى حالة ودية

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

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

public static string GetStringValue(this AuthenticationMethod value)
{
  string output = null;
  Type type = value.GetType();
  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;
}

يمكنك بعد ذلك الوصول إليه بسهولة من مثال التعداد:

Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());



أستخدم طريقة إضافة:

public static class AttributesHelperExtension
    {
        public static string ToDescription(this Enum value)
        {
            var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return da.Length > 0 ? da[0].Description : value.ToString();
        }
}

الآن قم بتزيين enum بـ:

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

عندما تتصل

AuthenticationMethod.FORMS.ToDescription() ستحصل على "FORMS" .




يمكنني استخدام السمة الوصف من مساحة الاسم System.ComponentModel. ببساطة قم بتزيين التعداد ثم استخدم هذا الرمز لاسترداده:

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)
                {
                    //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 Cycle : int
{        
   [Description("Daily Cycle")]
   Daily = 1,
   Weekly,
   Monthly
}

هذا الرمز يلائم بشكل جيد التعدادات حيث لا تحتاج إلى "اسم مألوف" وسيعود فقط .ToString () من التعداد.




أنا حقا أحب الإجابة ياكوب Šturc ، ولكن من عيب أنه لا يمكنك استخدامه مع بيان حالة التبديل. فيما يلي نسخة معدلة قليلاً من إجابته يمكن استخدامها مع عبارة التبديل:

public sealed class AuthenticationMethod
{
    #region This code never needs to change.
    private readonly string _name;
    public readonly Values Value;

    private AuthenticationMethod(Values value, String name){
        this._name = name;
        this.Value = value;
    }

    public override String ToString(){
        return _name;
    }
    #endregion

    public enum Values
    {
        Forms = 1,
        Windows = 2,
        SSN = 3
    }

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (Values.Forms, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (Values.Windows, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (Values.SSN, "SSN");
}

لذا تحصل على جميع فوائد إجابة ياكوب أوتورك ، بالإضافة إلى أنه يمكننا استخدامه مع عبارة التبديل مثل:

var authenticationMethodVariable = AuthenticationMethod.FORMS;  // Set the "enum" value we want to use.
var methodName = authenticationMethodVariable.ToString();       // Get the user-friendly "name" of the "enum" value.

// Perform logic based on which "enum" value was chosen.
switch (authenticationMethodVariable.Value)
{
    case authenticationMethodVariable.Values.Forms: // Do something
        break;
    case authenticationMethodVariable.Values.Windows: // Do something
        break;
    case authenticationMethodVariable.Values.SSN: // Do something
        break;      
}



باعتباري معظمكم ، أعجبتني الإجابة التي اختارتها Jakub Šturc ، لكنني أيضًا أكره حقًا نسخ الشفرة ، ومحاولة القيام بها بأقل قدر ممكن.

لذلك قررت أنني أردت الحصول على فئة EnumBase التي توارث بها معظم الوظائف / مدمجة ، مما يتركني للتركيز على المحتوى بدلاً من السلوك.

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

سأبدأ مع مثال ياكوب ، ولكن باستخدام الميراث والأدوية:

public sealed class AuthenticationMethod : EnumBase<AuthenticationMethod, int>
{
    public static readonly AuthenticationMethod FORMS =
        new AuthenticationMethod(1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION =
        new AuthenticationMethod(2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON =
        new AuthenticationMethod(3, "SSN");

    private AuthenticationMethod(int Value, String Name)
        : base( Value, Name ) { }
    public new static IEnumerable<AuthenticationMethod> All
    { get { return EnumBase<AuthenticationMethod, int>.All; } }
    public static explicit operator AuthenticationMethod(string str)
    { return Parse(str); }
}

وها هي الطبقة الأساسية:

using System;
using System.Collections.Generic;
using System.Linq; // for the .AsEnumerable() method call

// E is the derived type-safe-enum class
// - this allows all static members to be truly unique to the specific
//   derived class
public class EnumBase<E, T> where E: EnumBase<E, T>
{
    #region Instance code
    public T Value { get; private set; }
    public string Name { get; private set; }

    protected EnumBase(T EnumValue, string Name)
    {
        Value = EnumValue;
        this.Name = Name;
        mapping.Add(Name, this);
    }

    public override string ToString() { return Name; }
    #endregion

    #region Static tools
    static private readonly Dictionary<string, EnumBase<E, T>> mapping;
    static EnumBase() { mapping = new Dictionary<string, EnumBase<E, T>>(); }
    protected static E Parse(string name)
    {
        EnumBase<E, T> result;
        if (mapping.TryGetValue(name, out result))
        {
            return (E)result;
        }

        throw new InvalidCastException();
    }
    // This is protected to force the child class to expose it's own static
    // method.
    // By recreating this static method at the derived class, static
    // initialization will be explicit, promising the mapping dictionary
    // will never be empty when this method is called.
    protected static IEnumerable<E> All
    { get { return mapping.Values.AsEnumerable().Cast<E>(); } }
    #endregion
}



كيف حلت هذا كطريقة تمديد:

using System.ComponentModel;
public static string GetDescription(this Enum value)
{
    var descriptionAttribute = (DescriptionAttribute)value.GetType()
        .GetField(value.ToString())
        .GetCustomAttributes(false)
        .Where(a => a is DescriptionAttribute)
        .FirstOrDefault();

    return descriptionAttribute != null ? descriptionAttribute.Description : value.ToString();
}

التعداد:

public enum OrderType
{
    None = 0,
    [Description("New Card")]
    NewCard = 1,
    [Description("Reload")]
    Refill = 2
}

الاستخدام (حيث o.OrderType عبارة عن خاصية لها نفس الاسم مثل التعداد):

o.OrderType.GetDescription()

الذي يعطيني سلسلة من "البطاقة الجديدة" أو "إعادة تحميل" بدلاً من قيمة التضمين الفعلية NewCard و Refill.




إذا جئت إلى هنا لتتطلع إلى تطبيق "التعداد" البسيط ولكن قيمه عبارة عن سلاسل بدلاً من النتوءات ، فإليك أبسط حل:

    public sealed class MetricValueList
    {
        public static readonly string Brand = "A4082457-D467-E111-98DC-0026B9010912";
        public static readonly string Name = "B5B5E167-D467-E111-98DC-0026B9010912";
    }

التنفيذ:

var someStringVariable = MetricValueList.Brand;



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

[TypeConverter(typeof(CustomEnumTypeConverter(typeof(MyEnum))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,
  // This will be exposed exactly.
  Exact
}

يجب ان يكون

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

برلنت!




Option 1:

public sealed class FormsAuth
{
     public override string ToString{return "Forms Authtentication";}
}
public sealed class WindowsAuth
{
     public override string ToString{return "Windows Authtentication";}
}

public sealed class SsoAuth
{
     public override string ToString{return "SSO";}
}

وثم

object auth = new SsoAuth(); //or whatever

//...
//...
// blablabla

DoSomethingWithTheAuth(auth.ToString());

Option 2:

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

public class MyClass
{
    private Dictionary<AuthenticationMethod, String> map = new Dictionary<AuthenticationMethod, String>();
    public MyClass()
    {
         map.Add(AuthenticationMethod.FORMS,"Forms Authentication");
         map.Add(AuthenticationMethod.WINDOWSAUTHENTICATION ,"Windows Authentication");
         map.Add(AuthenticationMethod.SINGLESIGNON ,"SSo Authentication");
    }
}



for me, the pragmatic approach is class inside class, sample:

public class MSEModel
{
    class WITS
    {
        public const string DATE = "5005";
        public const string TIME = "5006";
        public const string MD = "5008";
        public const string ROP = "5075";
        public const string WOB = "5073";
        public const string RPM = "7001";
... 
    }



If I'm understanding you correctly, you can simply use .ToString() to retrieve the name of the enum from the value (Assuming it's already cast as the Enum); If you had the naked int (lets say from a database or something) you can first cast it to the enum. Both methods below will get you the enum name.

AuthenticationMethod myCurrentSetting = AuthenticationMethod.FORMS;
Console.WriteLine(myCurrentSetting); // Prints: FORMS
string name = Enum.GetNames(typeof(AuthenticationMethod))[(int)myCurrentSetting-1];
Console.WriteLine(name); // Prints: FORMS

Keep in mind though, the second technique assumes you are using ints and your index is 1 based (not 0 based). The function GetNames also is quite heavy by comparison, you are generating a whole array each time it's called. As you can see in the first technique, .ToString() is actually called implicitly. Both of these are already mentioned in the answers of course, I'm just trying to clarify the differences between them.







When I am in a situation like that I propose the solution below.

And as a consuming class you could have

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyApp.Dictionaries
{
    class Greek
    {

        public static readonly string Alpha = "Alpha";
        public static readonly string Beta = "Beta";
        public static readonly string Gamma = "Gamma";
        public static readonly string Delta = "Delta";


        private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();


        static Greek() {
            Dictionary.Add(1, Alpha);
            Dictionary.Add(2, Beta);
            Dictionary.Add(3, Gamma);
            Dictionary.Add(4, Delta);
        }

        public static string getById(int id){
            return Dictionary.GetByFirst(id);
        }

        public static int getByValue(string value)
        {
            return Dictionary.GetBySecond(value);
        }

    }
}

And using a bidirectional dictionary: Based on this ( https://.com/a/255638/986160 ) assuming that the keys will be associated with single values in the dictionary and similar to ( https://.com/a/255630/986160 ) but a bit more elegant. This dictionary is also enumerable and you can go back and forth from ints to strings. Also you don't have to have any string in your codebase with the exception of this class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace MyApp.Dictionaries
{

    class BiDictionary<TFirst, TSecond> : IEnumerable
    {
        IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
        IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

        public void Add(TFirst first, TSecond second)
        {
            firstToSecond.Add(first, second);
            secondToFirst.Add(second, first);
        }

        public TSecond this[TFirst first]
        {
            get { return GetByFirst(first); }
        }

        public TFirst this[TSecond second]
        {
            get { return GetBySecond(second); }
        }

        public TSecond GetByFirst(TFirst first)
        {
            return firstToSecond[first];
        }

        public TFirst GetBySecond(TSecond second)
        {
            return secondToFirst[second];
        }

        public IEnumerator GetEnumerator()
        {
            return GetFirstEnumerator();
        }

        public IEnumerator GetFirstEnumerator()
        {
            return firstToSecond.GetEnumerator();
        }

        public IEnumerator GetSecondEnumerator()
        {
            return secondToFirst.GetEnumerator();
        }
    }
}






Related

c# enums

Tags

c#   enums