سؤال

ما هي أفضل طريقة لتحويل سلسلة إلى قيمة تعداد في C#؟

لدي علامة تحديد HTML تحتوي على قيم التعداد.عندما يتم نشر الصفحة، أريد التقاط القيمة (التي ستكون على شكل سلسلة) وتحويلها إلى قيمة التعداد.

في عالم مثالي، يمكنني أن أفعل شيئًا كهذا:

StatusEnum MyStatus = StatusEnum.Parse("Active");

لكن هذا ليس رمزًا صالحًا.

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

المحلول

في .NET Core و.NET>4 هناك طريقة تحليل عامة:

Enum.TryParse("Active", out StatusEnum myStatus);

يتضمن هذا أيضًا السطر الجديد لـ C#7 out المتغيرات، لذلك يقوم هذا بإجراء محاولة التحليل والتحويل إلى نوع التعداد الصريح والتهيئة+ملء myStatus عامل.

إذا كان لديك حق الوصول إلى C#7 وأحدث إصدار من .NET، فهذه هي الطريقة الأفضل.

الإجابة الأصلية

في .NET يكون الأمر قبيحًا إلى حد ما (حتى 4 أو أعلى):

StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum), "Active", true);

أميل إلى تبسيط هذا من خلال:

public static T ParseEnum<T>(string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

ثم يمكنني أن أفعل:

StatusEnum MyStatus = EnumUtil.ParseEnum<StatusEnum>("Active");

أحد الخيارات المقترحة في التعليقات هو إضافة ملحق، وهو أمر بسيط بما فيه الكفاية:

public static T ToEnum<T>(this string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

StatusEnum MyStatus = "Active".ToEnum<StatusEnum>();

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

public static T ToEnum<T>(this string value, T defaultValue) 
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    T result;
    return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}

مما يجعل هذه الدعوة:

StatusEnum MyStatus = "Active".ToEnum(StatusEnum.None);

ومع ذلك، سأكون حذرًا عند إضافة طريقة تمديد كهذه إلى string كـ (بدون التحكم في مساحة الاسم) سيظهر في جميع مثيلات string سواء كانوا يحملون التعداد أم لا (هكذا 1234.ToString().ToEnum(StatusEnum.None) سيكون صحيحا ولكن لا معنى له).غالبًا ما يكون من الأفضل تجنب ازدحام فئات Microsoft الأساسية بأساليب إضافية لا تنطبق إلا في سياقات محددة للغاية ما لم يكن لدى فريق التطوير بأكمله فهم جيد جدًا لما تفعله هذه الامتدادات.

نصائح أخرى

يستخدم Enum.TryParse<T>(String, T) (≥ .NET 4.0):

StatusEnum myStatus;
Enum.TryParse("Active", out myStatus);

يمكن تبسيطه بشكل أكبر باستخدام الإصدار C# 7.0 نوع المعلمة المضمنة:

Enum.TryParse("Active", out StatusEnum myStatus);

لاحظ أن أداء Enum.Parse() سيئ جدًا، لأنه يتم تنفيذه من خلال الانعكاس.(وينطبق الشيء نفسه على Enum.ToString، الذي يسير في الاتجاه الآخر.)

إذا كنت بحاجة إلى تحويل السلاسل إلى Enums في تعليمات برمجية حساسة للأداء، فإن أفضل رهان لك هو إنشاء ملف Dictionary<String,YourEnum> عند بدء التشغيل واستخدم ذلك لإجراء تحويلاتك.

أنت تبحث عن التعداد.تحليل.

SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");

يمكنك استخدام طرق التمديد الآن:

public static T ToEnum<T>(this string value, bool ignoreCase = true)
{
    return (T) Enum.Parse(typeof (T), value, ignoreCase);
}

ويمكنك الاتصال بهم عن طريق الرمز أدناه (هنا، FilterType هو نوع التعداد):

FilterType filterType = type.ToEnum<FilterType>();
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);

لذا، إذا كان لديك تعداد يسمى مزاج فسيبدو كما يلي:

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

   // ...
   Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
   Console.WriteLine("My mood is: {0}", m.ToString());

احذر:

enum Example
{
    One = 1,
    Two = 2,
    Three = 3
}

Enum.(Try)Parse() يقبل وسائط متعددة مفصولة بفواصل، ويجمعها مع الثنائي "أو" |.لا يمكنك تعطيل هذا وفي رأيي أنك لا تريده أبدًا.

var x = Enum.Parse("One,Two"); // x is now Three

حتى لو Three لم يتم تعريفها، x سيظل يحصل على قيمة int 3.وهذا أسوأ من ذلك:يمكن أن يمنحك Enum.Parse() قيمة لم يتم تعريفها حتى للتعداد!

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

بالإضافة إلى ذلك، وكما ذكر آخرون، فإن الأداء أقل من المثالي للتعدادات الكبيرة، أي الخطية في عدد القيم الممكنة.

أقترح ما يلي:

    public static bool TryParse<T>(string value, out T result)
        where T : struct
    {
        var cacheKey = "Enum_" + typeof(T).FullName;

        // [Use MemoryCache to retrieve or create&store a dictionary for this enum, permanently or temporarily.
        // [Implementation off-topic.]
        var enumDictionary = CacheHelper.GetCacheItem(cacheKey, CreateEnumDictionary<T>, EnumCacheExpiration);

        return enumDictionary.TryGetValue(value.Trim(), out result);
    }

    private static Dictionary<string, T> CreateEnumDictionary<T>()
    {
        return Enum.GetValues(typeof(T))
            .Cast<T>()
            .ToDictionary(value => value.ToString(), value => value, StringComparer.OrdinalIgnoreCase);
    }

التعداد.تحليل هو صديقك:

StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");

يمكنك تمديد الإجابة المقبولة بقيمة افتراضية لتجنب الاستثناءات:

public static T ParseEnum<T>(string value, T defaultValue) where T : struct
{
    try
    {
        T enumValue;
        if (!Enum.TryParse(value, true, out enumValue))
        {
            return defaultValue;
        }
        return enumValue;
    }
    catch (Exception)
    {
        return defaultValue;
    }
}

ثم تسميها مثل:

StatusEnum MyStatus = EnumUtil.ParseEnum("Active", StatusEnum.None);

لم نتمكن من افتراض إدخال صحيح تمامًا، واعتمدنا هذا الاختلاف في إجابة @Keith:

public static TEnum ParseEnum<TEnum>(string value) where TEnum : struct
{
    TEnum tmp; 
    if (!Enum.TryParse<TEnum>(value, true, out tmp))
    {
        tmp = new TEnum();
    }
    return tmp;
}
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str) 
{ 
    return (T) Enum.Parse(typeof(T), str);
}

يوزع السلسلة إلى TEnum بدون محاولة/التقاط وبدون طريقة TryParse() من .NET 4.5

/// <summary>
/// Parses string to TEnum without try/catch and .NET 4.5 TryParse()
/// </summary>
public static bool TryParseToEnum<TEnum>(string probablyEnumAsString_, out TEnum enumValue_) where TEnum : struct
{
    enumValue_ = (TEnum)Enum.GetValues(typeof(TEnum)).GetValue(0);
    if(!Enum.IsDefined(typeof(TEnum), probablyEnumAsString_))
        return false;

    enumValue_ = (TEnum) Enum.Parse(typeof(TEnum), probablyEnumAsString_);
    return true;
}

كود بسيط للغاية باستخدام TryParse:

var value = "Active";

StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
    status = StatusEnum.Unknown;

أنا أحب حل طريقة التمديد ..

namespace System
{
    public static class StringExtensions
    {

        public static bool TryParseAsEnum<T>(this string value, out T output) where T : struct
        {
            T result;

            var isEnum = Enum.TryParse(value, out result);

            output = isEnum ? result : default(T);

            return isEnum;
        }
    }
}

هنا أدناه تنفيذي مع الاختبارات.

using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
using static System.Console;

private enum Countries
    {
        NorthAmerica,
        Europe,
        Rusia,
        Brasil,
        China,
        Asia,
        Australia
    }

   [TestMethod]
        public void StringExtensions_On_TryParseAsEnum()
        {
            var countryName = "Rusia";

            Countries country;
            var isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsTrue(isCountry);
            AreEqual(Countries.Rusia, country);

            countryName = "Don't exist";

            isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsFalse(isCountry);
            AreEqual(Countries.NorthAmerica, country); // the 1rst one in the enumeration
        }
public static T ParseEnum<T>(string value)            //function declaration  
{
    return (T) Enum.Parse(typeof(T), value);
}

Importance imp = EnumUtil.ParseEnum<Importance>("Active");   //function call

===================================================================================================

using System;

class Program
{
    enum PetType
    {
    None,
    Cat = 1,
    Dog = 2
    }

    static void Main()
    {

    // Possible user input:
    string value = "Dog";

    // Try to convert the string to an enum:
    PetType pet = (PetType)Enum.Parse(typeof(PetType), value);

    // See if the conversion succeeded:
    if (pet == PetType.Dog)
    {
        Console.WriteLine("Equals dog.");
    }
    }
}
-------------
Output

Equals dog.

لقد استخدمت فئة (إصدار مكتوب بقوة من Enum مع تحسينات في التحليل والأداء).لقد وجدته على GitHub، ومن المفترض أن يعمل مع .NET 3.5 أيضًا.يحتوي على بعض الحمل الزائد للذاكرة نظرًا لأنه يقوم بتخزين القاموس مؤقتًا.

StatusEnum MyStatus = Enum<StatusEnum>.Parse("Active");

المدونة هي التعدادات - بناء جملة أفضل وأداء محسّن وTryParse في NET 3.5.

والكود:https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs

بالنسبة للأداء قد يساعد هذا:

    private static Dictionary<Type, Dictionary<string, object>> dicEnum = new Dictionary<Type, Dictionary<string, object>>();
    public static T ToEnum<T>(this string value, T defaultValue)
    {
        var t = typeof(T);
        Dictionary<string, object> dic;
        if (!dicEnum.ContainsKey(t))
        {
            dic = new Dictionary<string, object>();
            dicEnum.Add(t, dic);
            foreach (var en in Enum.GetValues(t))
                dic.Add(en.ToString(), en);
        }
        else
            dic = dicEnum[t];
        if (!dic.ContainsKey(value))
            return defaultValue;
        else
            return (T)dic[value];
    }

لقد وجدت أنه لم يتم أخذ حالة قيم التعداد التي تحتوي على قيمة EnumMember في الاعتبار هنا.حسنا هيا بنا:

using System.Runtime.Serialization;

public static TEnum ToEnum<TEnum>(this string value, TEnum defaultValue) where TEnum : struct
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    TEnum result;
    var enumType = typeof(TEnum);
    foreach (var enumName in Enum.GetNames(enumType))
    {
        var fieldInfo = enumType.GetField(enumName);
        var enumMemberAttribute = ((EnumMemberAttribute[]) fieldInfo.GetCustomAttributes(typeof(EnumMemberAttribute), true)).FirstOrDefault();
        if (enumMemberAttribute?.Value == value)
        {
            return Enum.TryParse(enumName, true, out result) ? result : defaultValue;
        }
    }

    return Enum.TryParse(value, true, out result) ? result : defaultValue;
}

ومثال على هذا التعداد:

public enum OracleInstanceStatus
{
    Unknown = -1,
    Started = 1,
    Mounted = 2,
    Open = 3,
    [EnumMember(Value = "OPEN MIGRATE")]
    OpenMigrate = 4
}

يجب عليك استخدام Enum.Parse للحصول على قيمة الكائن من Enum، وبعد ذلك يجب عليك تغيير قيمة الكائن إلى قيمة تعداد محددة.يمكن إجراء الإرسال إلى قيمة التعداد باستخدام Convert.ChangeType.يرجى إلقاء نظرة على مقتطف الشفرة التالي

public T ConvertStringValueToEnum<T>(string valueToParse){
    return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}

جرب هذه العينة:

 public static T GetEnum<T>(string model)
    {
        var newModel = GetStringForEnum(model);

        if (!Enum.IsDefined(typeof(T), newModel))
        {
            return (T)Enum.Parse(typeof(T), "None", true);
        }

        return (T)Enum.Parse(typeof(T), newModel.Result, true);
    }

    private static Task<string> GetStringForEnum(string model)
    {
        return Task.Run(() =>
        {
            Regex rgx = new Regex("[^a-zA-Z0-9 -]");
            var nonAlphanumericData = rgx.Matches(model);
            if (nonAlphanumericData.Count < 1)
            {
                return model;
            }
            foreach (var item in nonAlphanumericData)
            {
                model = model.Replace((string)item, "");
            }
            return model;
        });
    }

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

        <Extension()>
    Public Function ToEnum(Of TEnum)(ByVal value As String, ByVal defaultValue As TEnum) As TEnum
        If String.IsNullOrEmpty(value) Then
            Return defaultValue
        End If

        Return [Enum].Parse(GetType(TEnum), value, True)
    End Function
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){
if (string.IsNullOrEmpty(value))
    return defaultValue;

return Enum.Parse(typeof(TEnum), value, true);}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top