سؤال

أنا بحاجة إلى التحقق من صحة عدد صحيح أن أعرف إذا كان هو صالح التعداد القيمة.

ما هي أفضل طريقة للقيام بذلك في C# ؟

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

المحلول

يجب أن أحب هؤلاء القوم الذين نفترض أن البيانات ليس فقط دائما يأتي من واجهة المستخدم, ولكن واجهة المستخدم ضمن التحكم الخاصة بك!

IsDefined هو على ما يرام بالنسبة لمعظم سيناريوهات يمكن أن تبدأ مع:

public static bool TryParseEnum<TEnum>(this int enumValue, out TEnum retVal)
{
 retVal = default(TEnum);
 bool success = Enum.IsDefined(typeof(TEnum), enumValue);
 if (success)
 {
  retVal = (TEnum)Enum.ToObject(typeof(TEnum), enumValue);
 }
 return success;
}

(ومن الواضح أن مجرد قطرة 'هذا' إذا كنت لا أعتقد أنها مناسبة الباحث ملحق)

نصائح أخرى

IMHO بعد وضع علامة الإجابة غير صحيحة.
المعلمة و التحقق من صحة البيانات هي واحدة من الأشياء التي تم حفرها في لي منذ عقود.

لماذا

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

حيث

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

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

إجراء المكالمات إلى أي شيء في النظام.التعداد بطيئة جدا أنه كان ملحوظ الأداء عقوبة على الوظائف التي وردت المئات أو الآلاف من الكائنات التي لديها واحد أو أكثر من enums في خصائصها التي كان لا بد من التحقق من صحة حدود.

خلاصة القول, البقاء بعيدا عن كل شيء في النظام.التعداد الدرجة عند التحقق من صحة التعداد القيم بشكل مخيف بطيئة.

نتيجة

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

أنا أعرف واحد أو اثنين من الثوابت التي هي العليا و (اختياريا) أقل حدود التعداد ، واستخدامها في زوج من إذا() التحقق من صحة البيانات.
واحد الجانب السلبي هو أنه يجب أن تكون على يقين من أن تحديث الثوابت إذا قمت بتغيير enum.
هذا الأسلوب أيضا يعمل فقط إذا كان التعداد هو "تلقائي" على غرار حيث كل التعداد عنصر تدريجي قيمة عدد صحيح مثل 0,1,2,3,4,....أنها لن تعمل بشكل صحيح مع أعلام أو enums أن القيم التي ليست تدريجي.

نلاحظ أيضا أن هذا الأسلوب هو تقريبا بالسرعة العادية إذا "<"">" على العادية int32s (الذي سجل 38,000 القراد على الاختبارات).

على سبيل المثال:

public const MyEnum MYENUM_MINIMUM = MyEnum.One;
public const MyEnum MYENUM_MAXIMUM = MyEnum.Four;

public enum MyEnum
{
    One,
    Two,
    Three,
    Four
};

public static MyEnum Validate(MyEnum value)
{
    if (value < MYENUM_MINIMUM) { return MYENUM_MINIMUM; }
    if (value > MYENUM_MAXIMUM) { return MYENUM_MAXIMUM; }
    return value;
}

الأداء

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

التنميط أجريت على الإفراج عن تجميع في حلقة من مليون مرة في كل أسلوب مع مجموعة عشوائية صحيح قيمة المدخلات.كل اختبار ركض أكثر من 10 مرات في المتوسط.القراد النتائج تشمل مجموع الوقت لتنفيذ والتي سوف تشمل توليد رقم عشوائي.... الخولكن هذه سوف تكون ثابتة في جميع الاختبارات.1 القراد = 10ns.

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

المدرجة الأبطأ إلى الأسرع مع تقريب النتائج ، نأمل أن لا الأخطاء المطبعية.

حدود تحديدها في طريقة = 13,600,000 القراد

public static T Clamp<T>(T value)
{
    int minimum = Enum.GetValues(typeof(T)).GetLowerBound(0);
    int maximum = Enum.GetValues(typeof(T)).GetUpperBound(0);

    if (Convert.ToInt32(value) < minimum) { return (T)Enum.ToObject(typeof(T), minimum); }
    if (Convert.ToInt32(value) > maximum) { return (T)Enum.ToObject(typeof(T), maximum); }
    return value;
}

Enum.IsDefined = 1,800,000 القراد
ملاحظة:رمز هذا الإصدار لا المشبك مين/ماكس ولكن يعود الافتراضي إذا كان خارج الحدود.

public static T ValidateItem<T>(T eEnumItem)
{
    if (Enum.IsDefined(typeof(T), eEnumItem) == true)
        return eEnumItem;
    else
        return default(T);
}

النظام.التعداد تحويل Int32 مع يلقي = 1,800,000 القراد

public static Enum Clamp(this Enum value, Enum minimum, Enum maximum)
{
    if (Convert.ToInt32(value) < Convert.ToInt32(minimum)) { return minimum; }
    if (Convert.ToInt32(value) > Convert.ToInt32(maximum)) { return maximum; }
    return value;
}

إذا() مين/ماكس الثوابت = 43,000 القراد = الفائز من قبل 42x و 316x أسرع.

public static MyEnum Clamp(MyEnum value)
{
    if (value < MYENUM_MINIMUM) { return MYENUM_MINIMUM; }
    if (value > MYENUM_MAXIMUM) { return MYENUM_MAXIMUM; }
    return value;
}

-موسوعة الحياة-

كما الآخرين قد ذكر ، Enum.IsDefined بطيء شيء يجب أن تكون على علم إذا كان في حلقة.

عندما تفعل عدة مقارنات ، أسرع طريقة أولا يتم وضع القيم في HashSet.ثم ببساطة استخدام Contains للتحقق من ما إذا كانت قيمة صحيحة مثل:

int userInput = 4;
// below, Enum.GetValues converts enum to array. We then convert the array to hashset.
HashSet<int> validVals = new HashSet<int>((int[])Enum.GetValues(typeof(MyEnum)));
// the following could be in a loop, or do multiple comparisons, etc.
if (validVals.Contains(userInput))
{
    // is valid
}

براد أبرامز تحديدا يحذر Enum.IsDefined في منصبه خطر التبسيط.

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

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

إذا كان لديك مهمة الأداء الحاسمة المسألة حيث بطيئة ولكن وظيفية رمز يتم تشغيل في حلقة ضيقة ثم أنا شخصيا أنظر التحرك الذي رمز من الحلقة إن أمكن بدلا من حل عن طريق خفض وظائف.تقييد رمز فقط دعم متجاورة enums يمكن أن يكون كابوسا وجدت علة إذا ، على سبيل المثال, شخص ما في المستقبل تقرر استنكر بعض التعداد القيم.مبسط هل يمكن أن مجرد دعوة Enum.GetValues مرة واحدة في البداية لتجنب اثار كل انعكاس ، وما إلى ذلك آلاف المرات.هذا يجب أن تعطيك فوري زيادة في الأداء.إذا كنت بحاجة إلى المزيد من الأداء و أنت تعلم أن الكثير من enums متجاورة (ولكن كنت لا تزال ترغب في دعم 'gappy' enums) هل يمكن أن تذهب إلى مرحلة أكثر و لا شيء من هذا القبيل:

public abstract class EnumValidator<TEnum> where TEnum : struct, IConvertible
{
    protected static bool IsContiguous
    {
        get
        {
            int[] enumVals = Enum.GetValues(typeof(TEnum)).Cast<int>().ToArray();

            int lowest = enumVals.OrderBy(i => i).First();
            int highest = enumVals.OrderByDescending(i => i).First();

            return !Enumerable.Range(lowest, highest).Except(enumVals).Any();
        }
    }

    public static EnumValidator<TEnum> Create()
    {
        if (!typeof(TEnum).IsEnum)
        {
            throw new ArgumentException("Please use an enum!");
        }

        return IsContiguous ? (EnumValidator<TEnum>)new ContiguousEnumValidator<TEnum>() : new JumbledEnumValidator<TEnum>();
    }

    public abstract bool IsValid(int value);
}

public class JumbledEnumValidator<TEnum> : EnumValidator<TEnum> where TEnum : struct, IConvertible
{
    private readonly int[] _values;

    public JumbledEnumValidator()
    {
        _values = Enum.GetValues(typeof (TEnum)).Cast<int>().ToArray();
    }

    public override bool IsValid(int value)
    {
        return _values.Contains(value);
    }
}

public class ContiguousEnumValidator<TEnum> : EnumValidator<TEnum> where TEnum : struct, IConvertible
{
    private readonly int _highest;
    private readonly int _lowest;

    public ContiguousEnumValidator()
    {
        List<int> enumVals = Enum.GetValues(typeof (TEnum)).Cast<int>().ToList();

        _lowest = enumVals.OrderBy(i => i).First();
        _highest = enumVals.OrderByDescending(i => i).First();
    }

    public override bool IsValid(int value)
    {
        return value >= _lowest && value <= _highest;
    }
}

حيث حلقة الخاص بك يصبح شيئا مثل:

//Pre import-loop
EnumValidator< MyEnum > enumValidator = EnumValidator< MyEnum >.Create();
while(import)   //Tight RT loop.
{
    bool isValid = enumValidator.IsValid(theValue);
}

أنا متأكد EnumValidator الطبقات يمكن كتابة أكثر كفاءة (إنه سريع هاك إظهار) ولكن بصراحة من يهتم بما يحدث خارج استيراد الحلقة ؟ الشيء الوحيد الذي يحتاج إلى أن يكون بسرعة فائقة داخل الحلقة.هذا كان السبب في أخذ خلاصة الدرجة الطريق لتجنب لا لزوم لها إذا enumContiguous ثم آخر في حلقة (المصنع خلق أساسا لا يفعل هذا مقدما).سوف نلاحظ نوعا من النفاق ، للإيجاز هذا القانون يقيد وظيفة الباحث-enums.يجب أن تكون الاستفادة من IConvertible بدلا من استخدام الباحث مباشرة ولكن هذا الجواب هو بالفعل الالفاظ بما فيه الكفاية!

هذا هو كيف نفعل ذلك بناء على مشاركات متعددة على الانترنت.والسبب في ذلك هو التأكد من enums ملحوظ مع Flags السمة يمكن أيضا بنجاح التحقق من صحتها.

public static TEnum ParseEnum<TEnum>(string valueString, string parameterName = null)
{
    var parsed = (TEnum)Enum.Parse(typeof(TEnum), valueString, true);
    decimal d;
    if (!decimal.TryParse(parsed.ToString(), out d))
    {
        return parsed;
    }

    if (!string.IsNullOrEmpty(parameterName))
    {
        throw new ArgumentException(string.Format("Bad parameter value. Name: {0}, value: {1}", parameterName, valueString), parameterName);
    }
    else
    {
        throw new ArgumentException("Bad value. Value: " + valueString);
    }
}

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

(ENUMTYPE)Enum.ToObject(typeof(ENUMTYPE), INT)

أنا استقر على هذا الحل بأنه مزيج من الأداء والراحة الصيانة.

public enum DogBreed
{
    Unknown = 0,
    Beagle = 1,
    Labrador = 2,
    PeruvianIncaOrchid = 3,
}
public static class DogBreedExtensions
{
    public static bool IsValidDogBreed(this DogBreed breed)
    {
        var v = (int)breed;
        return v >= 1 && v <= 3;
    }
}

واستخدامها على النحو التالي:

var goodInput = 2;
var goodDog = (DogBreed)goodInput;
goodDog.IsValidDogBreed(); // true.

var badInput = 7;
var badDog = (DogBreed)badInput; // no problem, bad data here we come.
badDog.IsValidDogBreed(); // false, you're in the doghouse!

في حالتي ، داعيا Enum.IsDefined فقط الحصول على لي في منتصف الطريق, كما عادة ما ترغب في رفض "غير معروف" enum القيمة.

هذا النهج يتجنب مشاكل الأداء من التعداد.IsDefined ويعرف قاعدة التحقق من الصحة على مقربة من التعداد الذي أحب.

طريقة التمديد بسهولة للتكيف مع المتطلبات المتغيرة و يمكن تطبيقها على الباحث إذا رغبت في ذلك - (أي public static bool IsValidDogBreed(this int breed))

هنا هو عام الحل باستخدام ثابت-constucted HashSet<T>.

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

public static class EnumHelpers
{
    /// <summary>
    /// Returns whether the given enum value is a defined value for its type.
    /// Throws if the type parameter is not an enum type.
    /// </summary>
    public static bool IsDefined<T>(T enumValue)
    {
        if (typeof(T).BaseType != typeof(System.Enum)) throw new ArgumentException($"{nameof(T)} must be an enum type.");

        return EnumValueCache<T>.DefinedValues.Contains(enumValue);
    }

    /// <summary>
    /// Statically caches each defined value for each enum type for which this class is accessed.
    /// Uses the fact that static things exist separately for each distinct type parameter.
    /// </summary>
    internal static class EnumValueCache<T>
    {
        public static HashSet<T> DefinedValues { get; }

        static EnumValueCache()
        {
            if (typeof(T).BaseType != typeof(System.Enum)) throw new Exception($"{nameof(T)} must be an enum type.");

            DefinedValues = new HashSet<T>((T[])System.Enum.GetValues(typeof(T)));
        }
    }
}

علما أن هذا النهج هو تمديدها بسهولة إلى التعداد تحليل وكذلك باستخدام القاموس مع سلسلة مفاتيح (التدبير حالة عدم الاكتراث و سلسلة رقمية تمثيل).

للتحقق إذا كانت قيمة قيمة صالحة في التعداد ، تحتاج فقط إلى استدعاء أسلوب ثابت Enum.IsDefined.

int value = 99;//Your int value
if (Enum.IsDefined(typeof(your_enum_type), value))
{
   //Todo when value is valid
}else{
   //Todo when value is not valid
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top