c# - إنشاء أسلوب عام تقييد تي إلى تعداد




generics enums (12)

أنا بناء وظيفة لتوسيع مفهوم 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".

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

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

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

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


أنا أحب حل كريستوفر Currens باستخدام 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;
}

أنا خلقت تمديد الأسلوب 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

إذا كان لا بأس من استخدام الصب المباشر بعد ذلك ، أعتقد أنه يمكنك استخدام الفئة الأساسية 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 # <= 7.2. ومع ذلك ، هناك طلب ميزة لغة C # (مرتبطة بطلب ميزة corefx ) للسماح بما يلي ؛

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

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

تصحيح

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


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

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

لقد تأخرت عن اللعب ، لكني كنت أعتبره تحديًا لمعرفة كيف يمكن القيام به. من غير الممكن في 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 على النحو التالي:

ilasm.exe /DLL /OUTPUT=MyThing.netmodule

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

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

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

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

لقد قمت بتعديل العينة بواسطة 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);
    }
}

لقد قمت بتغليف حل 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);
        }
    }
}

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

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

نظرًا لأن نوع 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");
   }

   //...
}

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


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

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 لجعل القيد نوع المرجع. بشكل فعال يحد هذا من T إلى أنواع System.Object و System.ValueType و System.Enum وأي نوع واجهة.


يمكنك تعريف منشئ ثابت للفئة التي ستتحقق من أن النوع T هو تعداد ورمي استثناء إذا لم يكن. هذه هي الطريقة التي ذكرها Jeffery Richter في كتابه 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) ، الإدخال ، true) للتحويل من السلسلة إلى التعداد. المعلمة true الأخيرة هي تجاهل حالة الإدخال.





enums