سؤال

أقوم ببناء وظيفة لتوسيع نطاق Enum.Parse مفهوم ذلك

  • يسمح بتحليل القيمة الافتراضية في حالة عدم العثور على قيمة التعداد
  • غير حساس لحالة الأحرف

لذلك كتبت ما يلي:

public static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
{
    if (string.IsNullOrEmpty(value)) return defaultValue;
    foreach (T item in Enum.GetValues(typeof(T)))
    {
        if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
    }
    return defaultValue;
}

أحصل على خطأ القيد لا يمكن أن يكون فئة خاصة System.Enum.

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

يحرر لقد حظيت جميع الاقتراحات أدناه بتقدير كبير، شكرًا.

لقد استقرت على (لقد تركت الحلقة للحفاظ على عدم حساسية حالة الأحرف - أنا أستخدم هذا عند تحليل XML)

public static class EnumUtils
{
    public static T ParseEnum<T>(string value, T defaultValue) where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum) throw new ArgumentException("T must be an enumerated type");
        if (string.IsNullOrEmpty(value)) return defaultValue;

        foreach (T item in Enum.GetValues(typeof(T)))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

يحرر: (16 فبراير 2015) نشر جوليان ليبوسكوين مؤخرًا يقوم المترجم بفرض حل عام آمن من النوع في MSIL أو F# أدناه، وهو أمر يستحق المشاهدة والتصويت الإيجابي.سأقوم بإزالة هذا التعديل إذا ظهر الحل في أعلى الصفحة.

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

المحلول

منذ Enum اكتب الأدوات IConvertible الواجهة، يجب أن يكون التنفيذ الأفضل شيئًا مثل هذا:

public T GetEnumFromString<T>(string value) where T : struct, IConvertible
{
   if (!typeof(T).IsEnum) 
   {
      throw new ArgumentException("T must be an enumerated type");
   }

   //...
}

سيظل هذا يسمح بتمرير تنفيذ أنواع القيم IConvertible.الفرص نادرة بالرغم من ذلك.

نصائح أخرى

تم دعم هذه الميزة أخيرًا في الإصدار C# 7.3!

المقتطف التالي (من عينات الدوت نت) يوضح كيف:

public static Dictionary<int, string> EnumNamedValues<T>() where T : System.Enum
{
    var result = new Dictionary<int, string>();
    var values = Enum.GetValues(typeof(T));

    foreach (int item in values)
        result.Add(item, Enum.GetName(typeof(T), item));
    return result;
}

تأكد من ضبط إصدار لغتك في مشروع C# الخاص بك على الإصدار 7.3.


الإجابة الأصلية أدناه:

لقد تأخرت في الوصول إلى اللعبة، لكنني اعتبرتها تحديًا لمعرفة كيفية القيام بذلك.هذا غير ممكن في C# (أو VB.NET، ولكن قم بالتمرير لأسفل للوصول إلى F#)، ولكن ممكن في MSIL.لقد كتبت هذا الشيء الصغير...

// license: http://www.apache.org/licenses/LICENSE-2.0.html
.assembly MyThing{}
.class public abstract sealed MyThing.Thing
       extends [mscorlib]System.Object
{
  .method public static !!T  GetEnumFromString<valuetype .ctor ([mscorlib]System.Enum) T>(string strValue,
                                                                                          !!T defaultValue) cil managed
  {
    .maxstack  2
    .locals init ([0] !!T temp,
                  [1] !!T return_value,
                  [2] class [mscorlib]System.Collections.IEnumerator enumerator,
                  [3] class [mscorlib]System.IDisposable disposer)
    // if(string.IsNullOrEmpty(strValue)) return defaultValue;
    ldarg strValue
    call bool [mscorlib]System.String::IsNullOrEmpty(string)
    brfalse.s HASVALUE
    br RETURNDEF         // return default it empty

    // foreach (T item in Enum.GetValues(typeof(T)))
  HASVALUE:
    // Enum.GetValues.GetEnumerator()
    ldtoken !!T
    call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
    call class [mscorlib]System.Array [mscorlib]System.Enum::GetValues(class [mscorlib]System.Type)
    callvirt instance class [mscorlib]System.Collections.IEnumerator [mscorlib]System.Array::GetEnumerator() 
    stloc enumerator
    .try
    {
      CONDITION:
        ldloc enumerator
        callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()
        brfalse.s LEAVE

      STATEMENTS:
        // T item = (T)Enumerator.Current
        ldloc enumerator
        callvirt instance object [mscorlib]System.Collections.IEnumerator::get_Current()
        unbox.any !!T
        stloc temp
        ldloca.s temp
        constrained. !!T

        // if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        callvirt instance string [mscorlib]System.Object::ToString()
        callvirt instance string [mscorlib]System.String::ToLower()
        ldarg strValue
        callvirt instance string [mscorlib]System.String::Trim()
        callvirt instance string [mscorlib]System.String::ToLower()
        callvirt instance bool [mscorlib]System.String::Equals(string)
        brfalse.s CONDITION
        ldloc temp
        stloc return_value
        leave.s RETURNVAL

      LEAVE:
        leave.s RETURNDEF
    }
    finally
    {
        // ArrayList's Enumerator may or may not inherit from IDisposable
        ldloc enumerator
        isinst [mscorlib]System.IDisposable
        stloc.s disposer
        ldloc.s disposer
        ldnull
        ceq
        brtrue.s LEAVEFINALLY
        ldloc.s disposer
        callvirt instance void [mscorlib]System.IDisposable::Dispose()
      LEAVEFINALLY:
        endfinally
    }

  RETURNDEF:
    ldarg defaultValue
    stloc return_value

  RETURNVAL:
    ldloc return_value
    ret
  }
} 

الذي يولد وظيفة ذلك كان تبدو هكذا، إذا كانت صالحة C#:

T GetEnumFromString<T>(string valueString, T defaultValue) where T : Enum

ثم باستخدام كود C# التالي:

using MyThing;
// stuff...
private enum MyEnum { Yes, No, Okay }
static void Main(string[] args)
{
    Thing.GetEnumFromString("No", MyEnum.Yes); // returns MyEnum.No
    Thing.GetEnumFromString("Invalid", MyEnum.Okay);  // returns MyEnum.Okay
    Thing.GetEnumFromString("AnotherInvalid", 0); // compiler error, not an Enum
}

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

عن طريق إزالة الخط .assembly MyThing{} واستدعاء الإسلام على النحو التالي:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

تحصل على netmodule بدلاً من التجميع.

لسوء الحظ، VS2010 (والإصدارات الأقدم بوضوح) لا يدعم إضافة مراجع netmodule، مما يعني أنه سيتعين عليك تركها في مجموعتين منفصلتين عند تصحيح الأخطاء.الطريقة الوحيدة التي يمكنك من خلالها إضافتها كجزء من التجميع الخاص بك هي تشغيل csc.exe بنفسك باستخدام ملف /addmodule:{files} وسيطة سطر الأوامر.لن يكون كذلك أيضاً مؤلمة في البرنامج النصي MSBuild.بالطبع، إذا كنت شجاعًا أو غبيًا، فيمكنك تشغيل csc بنفسك يدويًا في كل مرة.ومن المؤكد أن الأمر يصبح أكثر تعقيدًا نظرًا لأن التجميعات المتعددة تحتاج إلى الوصول إليه.

لذلك، يمكن القيام بذلك في .Net.هل يستحق الجهد الإضافي؟أم، حسنًا، أعتقد أنني سأدعك تقرر ذلك.


الحل F# كبديل

رصيد إضافي:وتبين أن هناك قيود عامة على enum ممكن بلغة .NET أخرى على الأقل إلى جانب MSIL:F#.

type MyThing =
    static member GetEnumFromString<'T when 'T :> Enum> str defaultValue: 'T =
        /// protect for null (only required in interop with C#)
        let str = if isNull str then String.Empty else str

        Enum.GetValues(typedefof<'T>)
        |> Seq.cast<_>
        |> Seq.tryFind(fun v -> String.Compare(v.ToString(), str.Trim(), true) = 0)
        |> function Some x -> x | None -> defaultValue

من الأسهل الحفاظ على هذه اللغة لأنها لغة معروفة مع دعم كامل لـ Visual Studio IDE، لكنك لا تزال بحاجة إلى مشروع منفصل في الحل الخاص بك لذلك.ومع ذلك، فإنه ينتج بشكل طبيعي IL مختلفًا إلى حد كبير (الرمز يكون مختلفة جدًا) وتعتمد على FSharp.Core المكتبة، والتي، مثل أي مكتبة خارجية أخرى، يجب أن تصبح جزءًا من توزيعتك.

إليك كيفية استخدامه (أساسًا نفس حل MSIL)، ولإظهار فشله بشكل صحيح في البنيات المترادفة:

// works, result is inferred to have type StringComparison
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", StringComparison.Ordinal);
// type restriction is recognized by C#, this fails at compile time
var result = MyThing.GetEnumFromString("OrdinalIgnoreCase", 42);

ج # ≥ 7.3

بدءًا من الإصدار C# 7.3 (المتوفر مع Visual Studio 2017 ≥ v15.7)، أصبح هذا الرمز الآن صالحًا تمامًا:

public static TEnum Parse<TEnum>(string value)
where TEnum : struct, Enum { ... }

ج # ≥ 7.2

يمكن أن يكون لديك مترجم حقيقي يفرض قيد التعداد عن طريق إساءة استخدام وراثة القيد.يحدد التعليمة البرمجية التالية كلاً من أ class و أ struct القيود في نفس الوقت:

public abstract class EnumClassUtils<TClass>
where TClass : class
{

    public static TEnum Parse<TEnum>(string value)
    where TEnum : struct, TClass
    {
        return (TEnum) Enum.Parse(typeof(TEnum), value);
    }

}

public class EnumUtils : EnumClassUtils<Enum>
{
}

الاستخدام:

EnumUtils.Parse<SomeEnum>("value");

ملحوظة:تم ذكر ذلك على وجه التحديد في مواصفات لغة C# 5.0:

إذا كانت معلمة النوع S تعتمد على معلمة النوع T، فحينئذٍ:...] من المفيد أن يكون S لقيود نوع القيمة و T لإقامة نوع المرجع.على نحو فعال ، يحد هذا to the types system.object ، system.valuetype ، system.enum ، وأي نوع واجهة.

يحرر

لقد تمت الإجابة على هذا السؤال الآن بشكل رائع جوليان ليبوسكوين.وأود أيضا أن تمديد إجابته مع ignoreCase, defaultValue والوسائط الاختيارية، أثناء الإضافة TryParse و ParseOrDefault.

public abstract class ConstrainedEnumParser<TClass> where TClass : class
// value type constraint S ("TEnum") depends on reference type T ("TClass") [and on struct]
{
    // internal constructor, to prevent this class from being inherited outside this code
    internal ConstrainedEnumParser() {}
    // Parse using pragmatic/adhoc hard cast:
    //  - struct + class = enum
    //  - 'guaranteed' call from derived <System.Enum>-constrained type EnumUtils
    public static TEnum Parse<TEnum>(string value, bool ignoreCase = false) where TEnum : struct, TClass
    {
        return (TEnum)Enum.Parse(typeof(TEnum), value, ignoreCase);
    }
    public static bool TryParse<TEnum>(string value, out TEnum result, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        var didParse = Enum.TryParse(value, ignoreCase, out result);
        if (didParse == false)
        {
            result = defaultValue;
        }
        return didParse;
    }
    public static TEnum ParseOrDefault<TEnum>(string value, bool ignoreCase = false, TEnum defaultValue = default(TEnum)) where TEnum : struct, TClass // value type constraint S depending on T
    {
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum result;
        if (Enum.TryParse(value, ignoreCase, out result)) { return result; }
        return defaultValue;
    }
}

public class EnumUtils: ConstrainedEnumParser<System.Enum>
// reference type constraint to any <System.Enum>
{
    // call to parse will then contain constraint to specific <System.Enum>-class
}

أمثلة على الاستخدام:

WeekDay parsedDayOrArgumentException = EnumUtils.Parse<WeekDay>("monday", ignoreCase:true);
WeekDay parsedDayOrDefault;
bool didParse = EnumUtils.TryParse<WeekDay>("clubs", out parsedDayOrDefault, ignoreCase:true);
parsedDayOrDefault = EnumUtils.ParseOrDefault<WeekDay>("friday", ignoreCase:true, defaultValue:WeekDay.Sunday);

قديم

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

  • يستخدم TEnum من أجل الوضوح للمستخدمين
  • أضف المزيد من قيود الواجهة للتحقق من القيود الإضافية
  • يترك TryParse مقبض ignoreCase مع المعلمة الحالية (تم تقديمها في VS2010/.NET 4)
  • اختياريا استخدام عام default قيمة (تم تقديمه في VS2005/.Net 2)
  • يستخدم الحجج الاختيارية(تم تقديمه في VS2010/.Net 4) بالقيم الافتراضية لـ defaultValue و ignoreCase

مما أدى إلى:

public static class EnumUtils
{
    public static TEnum ParseEnum<TEnum>(this string value,
                                         bool ignoreCase = true,
                                         TEnum defaultValue = default(TEnum))
        where TEnum : struct,  IComparable, IFormattable, IConvertible
    {
        if ( ! typeof(TEnum).IsEnum) { throw new ArgumentException("TEnum must be an enumerated type"); }
        if (string.IsNullOrEmpty(value)) { return defaultValue; }
        TEnum lResult;
        if (Enum.TryParse(value, ignoreCase, out lResult)) { return lResult; }
        return defaultValue;
    }
}

يمكنك تحديد مُنشئ ثابت للفئة الذي سيتحقق من أن النوع T عبارة عن تعداد ويطرح استثناءً إذا لم يكن كذلك.هذه هي الطريقة التي ذكرها جيفري ريختر في كتابه CLR عبر C#.

internal sealed class GenericTypeThatRequiresAnEnum<T> {
    static GenericTypeThatRequiresAnEnum() {
        if (!typeof(T).IsEnum) {
        throw new ArgumentException("T must be an enumerated type");
        }
    }
}

ثم في طريقة التحليل، يمكنك فقط استخدام Enum.Parse(typeof(T), input, true) للتحويل من السلسلة إلى التعداد.المعلمة الحقيقية الأخيرة هي لتجاهل حالة الإدخال.

يجب أيضًا الأخذ في الاعتبار أنه نظرًا لأن إصدار C# 7.3 باستخدام قيود Enum أصبح مدعومًا خارج الصندوق دون الحاجة إلى إجراء عمليات فحص وأشياء إضافية.

لذا، وبالنظر إلى أنك قمت بتغيير إصدار اللغة لمشروعك إلى C# 7.3، فإن الكود التالي سيعمل بشكل جيد تمامًا:

    private static T GetEnumFromString<T>(string value, T defaultValue) where T : Enum
    {
        // Your code goes here...
    }

في حال كنت لا تعرف كيفية تغيير إصدار اللغة إلى C# 7.3، راجع لقطة الشاشة التالية:enter image description here

التعديل 1 - إصدار Visual Studio المطلوب والنظر في ReSharper

لكي يتعرف Visual Studio على بناء الجملة الجديد، تحتاج إلى الإصدار 15.7 على الأقل.يمكنك أن تجد ذلك مذكورًا أيضًا في ملاحظات إصدار Microsoft، راجع Visual Studio 2017 15.7 ملاحظات الإصدار.شكرًا @MohamedElshawaf على الإشارة إلى هذا السؤال الصحيح.

يرجى ملاحظة أيضًا أنه في حالتي ReSharper 2018.1 حتى كتابة هذا التعديل لا يدعم C# 7.3 حتى الآن.بعد تنشيط ReSharper، فإنه يسلط الضوء على قيد Enum كخطأ يخبرني لا يمكن استخدام "System.Array"، و"System.Delegate"، و"System.Enum"، و"System.ValueType"، و"object" كقيد لمعلمة النوع.يقترح ReSharper كحل سريع لـ قم بإزالة قيد "Enum" الخاص بمعلمة النوع T الخاصة بالأسلوب

ومع ذلك، إذا قمت بإيقاف تشغيل ReSharper مؤقتًا ضمن الأدوات -> الخيارات -> ReSharper Ultimate -> عام سترى أن بناء الجملة جيد تمامًا نظرًا لأنك تستخدم VS 15.7 أو أعلى وC# 7.3 أو أعلى.

لقد قمت بتعديل العينة بواسطة dimarzionist.سيعمل هذا الإصدار فقط مع Enums ولن يسمح للبنيات بالمرور.

public static T ParseEnum<T>(string enumString)
    where T : struct // enum 
    {
    if (String.IsNullOrEmpty(enumString) || !typeof(T).IsEnum)
       throw new Exception("Type given must be an Enum");
    try
    {

       return (T)Enum.Parse(typeof(T), enumString, true);
    }
    catch (Exception ex)
    {
       return default(T);
    }
}

حاولت تحسين الكود قليلاً:

public T LoadEnum<T>(string value, T defaultValue = default(T)) where T : struct, IComparable, IFormattable, IConvertible
{
    if (Enum.IsDefined(typeof(T), value))
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }
    return defaultValue;
}

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

public static class XmlEnumExtension
{
    public static string ReadXmlEnumAttribute(this Enum value)
    {
        if (value == null) throw new ArgumentNullException("value");
        var attribs = (XmlEnumAttribute[]) value.GetType().GetField(value.ToString()).GetCustomAttributes(typeof (XmlEnumAttribute), true);
        return attribs.Length > 0 ? attribs[0].Name : value.ToString();
    }

    public static T ParseXmlEnumAttribute<T>(this string str)
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            var attribs = (XmlEnumAttribute[])item.GetType().GetField(item.ToString()).GetCustomAttributes(typeof(XmlEnumAttribute), true);
            if(attribs.Length > 0 && attribs[0].Name.Equals(str)) return item;
        }
        return (T)Enum.Parse(typeof(T), str, true);
    }
}

public enum MyEnum
{
    [XmlEnum("First Value")]
    One,
    [XmlEnum("Second Value")]
    Two,
    Three
}

 static void Main()
 {
    // Parsing from XmlEnum attribute
    var str = "Second Value";
    var me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    // Parsing without XmlEnum
    str = "Three";
    me = str.ParseXmlEnumAttribute<MyEnum>();
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
    me = MyEnum.One;
    System.Console.WriteLine(me.ReadXmlEnumAttribute());
}

آمل أن يكون هذا مفيدًا:

public static TValue ParseEnum<TValue>(string value, TValue defaultValue)
                  where TValue : struct // enum 
{
      try
      {
            if (String.IsNullOrEmpty(value))
                  return defaultValue;
            return (TValue)Enum.Parse(typeof (TValue), value);
      }
      catch(Exception ex)
      {
            return defaultValue;
      }
}

ومن المثير للاهتمام، على ما يبدو أن هذا هو ممكن في لغات أخرى (إدارة C++، IL مباشرة).

يقتبس:

...ينتج كلا التقييدين في الواقع IL صالحًا ويمكن أيضًا استهلاكهما بواسطة C# إذا تمت كتابتهما بلغة أخرى (يمكنك الإعلان عن هذه القيود في لغة C++ المُدارة أو في IL).

من تعرف

هذا هو رأيي في ذلك.مجتمعة من الإجابات وMSDN

public static TEnum ParseToEnum<TEnum>(this string text) where TEnum : struct, IConvertible, IComparable, IFormattable
{
    if (string.IsNullOrEmpty(text) || !typeof(TEnum).IsEnum)
        throw new ArgumentException("TEnum must be an Enum type");

    try
    {
        var enumValue = (TEnum)Enum.Parse(typeof(TEnum), text.Trim(), true);
        return enumValue;
    }
    catch (Exception)
    {
        throw new ArgumentException(string.Format("{0} is not a member of the {1} enumeration.", text, typeof(TEnum).Name));
    }
}

مصدر MSDN

الإجابات الموجودة صحيحة اعتبارًا من C# <=7.2.ومع ذلك، هناك لغة C# طلب المواصفات (مرتبط ب com.corefx طلب الميزة) للسماح بما يلي؛

public class MyGeneric<TEnum> where TEnum : System.Enum
{ }

وفي وقت كتابة هذا التقرير، كانت الميزة "قيد المناقشة" في اجتماعات تطوير اللغة.

يحرر

حسب نوفلمعلومات، يتم تقديم هذا في C# 7.3.

لقد أحببت هذا دائمًا (يمكنك تعديله حسب الاقتضاء):

public static IEnumerable<TEnum> GetEnumValues()
{
  Type enumType = typeof(TEnum);

  if(!enumType.IsEnum)
    throw new ArgumentException("Type argument must be Enum type");

  Array enumValues = Enum.GetValues(enumType);
  return enumValues.Cast<TEnum>();
}

لقد أحببت حل كريستوفر كورينز باستخدام IL ولكن بالنسبة لأولئك الذين لا يريدون التعامل مع الأعمال الصعبة المتمثلة في تضمين MSIL في عملية الإنشاء الخاصة بهم، فقد كتبت وظيفة مماثلة في C#.

يرجى ملاحظة أنه لا يمكنك استخدام القيود العامة مثل where T : Enum لأن التعداد هو نوع خاص.لذلك لا بد لي من التحقق مما إذا كان النوع العام المعطى هو التعداد حقًا.

وظيفتي هي:

public static T GetEnumFromString<T>(string strValue, T defaultValue)
{
    // Check if it realy enum at runtime 
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Method GetEnumFromString can be used with enums only");

    if (!string.IsNullOrEmpty(strValue))
    {
        IEnumerator enumerator = Enum.GetValues(typeof(T)).GetEnumerator();
        while (enumerator.MoveNext())
        {
            T temp = (T)enumerator.Current;
            if (temp.ToString().ToLower().Equals(strValue.Trim().ToLower()))
                return temp;
        }
    }

    return defaultValue;
}

لقد قمت بتغليف حل Vivek في فئة أدوات مساعدة يمكنك إعادة استخدامها.يرجى ملاحظة أنه لا يزال يتعين عليك تحديد قيود النوع "حيث T :struct, IConvertible" على النوع الخاص بك.

using System;

internal static class EnumEnforcer
{
    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your type parameter {0} is an enum.",
                typeParameterName,
                methodName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="typeParameterName">Name of the type parameter.</param>
    /// <param name="methodName">Name of the method which accepted the parameter.</param>
    /// <param name="inputParameterName">Name of the input parameter of this page.</param>
    public static void EnforceIsEnum<T>(string typeParameterName, string methodName, string inputParameterName)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            string message = string.Format(
                "Generic parameter {0} in {1} method forces an enumerated type. Make sure your input parameter {2} is of correct type.",
                typeParameterName,
                methodName,
                inputParameterName);

            throw new ArgumentException(message);
        }
    }

    /// <summary>
    /// Makes sure that generic input parameter is of an enumerated type.
    /// </summary>
    /// <typeparam name="T">Type that should be checked.</typeparam>
    /// <param name="exceptionMessage">Message to show in case T is not an enum.</param>
    public static void EnforceIsEnum<T>(string exceptionMessage)
        where T : struct, IConvertible
    {
        if (!typeof(T).IsEnum)
        {
            throw new ArgumentException(exceptionMessage);
        }
    }
}

لقد قمت بإنشاء طريقة تمديد to get integer value from enum نلقي نظرة على تنفيذ الأسلوب

public static int ToInt<T>(this T soure) where T : IConvertible//enum
{
    if (typeof(T).IsEnum)
    {
        return (int) (IConvertible)soure;// the tricky part
    }
    //else
    //    throw new ArgumentException("T must be an enumerated type");
    return soure.ToInt32(CultureInfo.CurrentCulture);
}

هذا هو الاستخدام

MemberStatusEnum.Activated.ToInt()// using extension Method
(int) MemberStatusEnum.Activated //the ordinary way

كما ورد في الإجابات الأخرى من قبل؛في حين أنه لا يمكن التعبير عن ذلك في الكود المصدري، إلا أنه يمكن القيام به بالفعل على مستوى IL.@ كريستوفر كورينز إجابة يوضح كيف يفعل IL ذلك.

مع فوديالوظيفة الإضافية ExtraConstraints.Fody هناك طريقة بسيطة جدًا، مكتملة بأدوات البناء، لتحقيق ذلك.فقط قم بإضافة حزم nuget الخاصة بهم (Fody, ExtraConstraints.Fody) إلى مشروعك وأضف القيود كما يلي (مقتطف من الملف التمهيدي لـ ExtraConstraints):

public void MethodWithEnumConstraint<[EnumConstraint] T>() {...}

public void MethodWithTypeEnumConstraint<[EnumConstraint(typeof(ConsoleColor))] T>() {...}

وسيقوم Fody بإضافة IL اللازم ليكون القيد موجودًا.لاحظ أيضًا الميزة الإضافية لتقييد المفوضين:

public void MethodWithDelegateConstraint<[DelegateConstraint] T> ()
{...}

public void MethodWithTypeDelegateConstraint<[DelegateConstraint(typeof(Func<int>))] T> ()
{...}

فيما يتعلق بالتعدادات، قد ترغب أيضًا في ملاحظة ما هو مثير للاهتمام للغاية التعدادات.NET.

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

public static class EnumUtils
{
    public static Enum GetEnumFromString(string value, Enum defaultValue)
    {
        if (string.IsNullOrEmpty(value)) return defaultValue;
        foreach (Enum item in Enum.GetValues(defaultValue.GetType()))
        {
            if (item.ToString().ToLower().Equals(value.Trim().ToLower())) return item;
        }
        return defaultValue;
    }
}

ثم يمكنك استخدامه مثل:

var parsedOutput = (YourEnum)EnumUtils.GetEnumFromString(someString, YourEnum.DefaultValue);

فقط للاكتمال، ما يلي هو حل جافا.أنا متأكد من أنه يمكن فعل الشيء نفسه في C# أيضًا.إنه يتجنب الاضطرار إلى تحديد النوع في أي مكان في التعليمات البرمجية - بدلاً من ذلك، يمكنك تحديده في السلاسل التي تحاول تحليلها.

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

بدلاً من قبول قيمة السلسلة فقط، قم بقبول سلسلة تحتوي على كل من التعداد والقيمة في النموذج "enumeration.value".رمز العمل أدناه - يتطلب Java 1.8 أو إصدار أحدث.سيؤدي هذا أيضًا إلى جعل XML أكثر دقة كما هو الحال عندما ترى شيئًا مثل color = "Color.red" بدلاً من color = "red" فقط.

يمكنك استدعاء الأسلوب AcceptEnumeratedValue () بسلسلة تحتوي على اسم قيمة نقطة اسم التعداد.

تقوم الطريقة بإرجاع القيمة التعدادية الرسمية.

import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;


public class EnumFromString {

    enum NumberEnum {One, Two, Three};
    enum LetterEnum {A, B, C};


    Map<String, Function<String, ? extends Enum>> enumsByName = new HashMap<>();

    public static void main(String[] args) {
        EnumFromString efs = new EnumFromString();

        System.out.print("\nFirst string is NumberEnum.Two - enum is " + efs.acceptEnumeratedValue("NumberEnum.Two").name());
        System.out.print("\nSecond string is LetterEnum.B - enum is " + efs.acceptEnumeratedValue("LetterEnum.B").name());

    }

    public EnumFromString() {
        enumsByName.put("NumberEnum", s -> {return NumberEnum.valueOf(s);});
        enumsByName.put("LetterEnum", s -> {return LetterEnum.valueOf(s);});
    }

    public Enum acceptEnumeratedValue(String enumDotValue) {

        int pos = enumDotValue.indexOf(".");

        String enumName = enumDotValue.substring(0, pos);
        String value = enumDotValue.substring(pos + 1);

        Enum enumeratedValue = enumsByName.get(enumName).apply(value);

        return enumeratedValue;
    }


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