क्या यह जांचने का कोई तरीका है कि क्या int # में कानूनी रूप से फिट है?


167

मैंने कुछ SO पोस्ट पढ़े हैं और ऐसा लगता है कि सबसे बुनियादी ऑपरेशन गायब है।

public enum LoggingLevel
{
    Off = 0,
    Error = 1,
    Warning = 2,
    Info = 3,
    Debug = 4,
    Trace = 5
};

if (s == "LogLevel")
{
    _log.LogLevel = (LoggingLevel)Convert.ToInt32("78");
    _log.LogLevel = (LoggingLevel)Enum.Parse(typeof(LoggingLevel), "78");
    _log.WriteDebug(_log.LogLevel.ToString());
}

यह कोई अपवाद नहीं है, यह स्टोर करने में खुशी है 78। क्या किसी एनम में जाने वाले मूल्य को मान्य करने का एक तरीका है?


जवाबों:


271

Enum.IsDefined की जाँच करें

उपयोग:

if(Enum.IsDefined(typeof(MyEnum), value))
    MyEnum a = (MyEnum)value; 

यह उस पृष्ठ से उदाहरण है:

using System;    
[Flags] public enum PetType
{
   None = 0, Dog = 1, Cat = 2, Rodent = 4, Bird = 8, Reptile = 16, Other = 32
};

public class Example
{
   public static void Main()
   {
      object value;     
      // Call IsDefined with underlying integral value of member.
      value = 1;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with invalid underlying integral value.
      value = 64;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with string containing member name.
      value = "Rodent";
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with a variable of type PetType.
      value = PetType.Dog;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      value = PetType.Dog | PetType.Cat;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with uppercase member name.      
      value = "None";
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      value = "NONE";
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with combined value
      value = PetType.Dog | PetType.Bird;
      Console.WriteLine("{0:D}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      value = value.ToString();
      Console.WriteLine("{0:D}: {1}", value, Enum.IsDefined(typeof(PetType), value));
   }
}

उदाहरण निम्न आउटपुट प्रदर्शित करता है:

//       1: True
//       64: False
//       Rodent: True
//       Dog: True
//       Dog, Cat: False
//       None: True
//       NONE: False
//       9: False
//       Dog, Bird: False

@matti: "78" को जो भी संख्या प्रतिनिधित्व LoggingLevelमें स्टोरेज के रूप में उपयोग करता है, उसे रूपांतरित करें , फिर उसको एनम वैल्यू के रूप में प्रस्तुत करें LoggingLevel
thecoop

9
ऐसा लगता है कि IsDefinedबिटवाइस एनुम सदस्यों के लिए काम नहीं कर रहा है।
सईद नेमाटी

29

उपरोक्त समाधान [Flags]स्थितियों से नहीं निपटते हैं ।

नीचे दिए गए मेरे समाधान में कुछ प्रदर्शन समस्याएँ हो सकती हैं (मुझे यकीन है कि एक व्यक्ति विभिन्न तरीकों से अनुकूलन कर सकता है) लेकिन अनिवार्य रूप से यह हमेशा साबित होगा कि एक एनम मान मान्य है या नहीं

यह तीन मान्यताओं पर निर्भर करता है:

  • C # में Enum मान केवल int, कुछ और नहीं, की अनुमति है
  • C # में Enum नाम अक्षर वर्ण से शुरू होना चाहिए
  • कोई वैध ईनाम नाम माइनस साइन के साथ नहीं हो सकता है: -

किसी एनम (ध्वज या नहीं) का मिलान नहीं होने ToString()पर, एनम पर कॉल करना या तो intमान देता है। यदि एक अनुमत एनम मूल्य का मिलान किया जाता है, तो यह मैच के नाम को प्रिंट करेगा (तों)।

इसलिए:

[Flags]
enum WithFlags
{
    First = 1,
    Second = 2,
    Third = 4,
    Fourth = 8
}

((WithFlags)2).ToString() ==> "Second"
((WithFlags)(2 + 4)).ToString() ==> "Second, Third"
((WithFlags)20).ToString() ==> "20"

इन दो नियमों को ध्यान में रखते हुए, हम यह मान सकते हैं कि यदि .NET फ्रेमवर्क अपना काम सही ढंग से करता है, तो वैध एनुम के किसी भी कॉल के ToString()परिणामस्वरूप कुछ ऐसा होगा, जिसमें वर्णमाला का पहला वर्ण होता है:

public static bool IsValid<TEnum>(this TEnum enumValue)
    where TEnum : struct
{
    var firstChar = enumValue.ToString()[0];
    return (firstChar < '0' || firstChar > '9') && firstChar != '-';
}

कोई इसे "हैक" कह सकता है, लेकिन लाभ यह है कि Microsoft के स्वयं के कार्यान्वयन पर Enumऔर C # मानकों पर भरोसा करके, आप अपने स्वयं के संभावित छोटी गाड़ी कोड या चेक पर भरोसा नहीं कर रहे हैं। उन परिस्थितियों में जहां प्रदर्शन असाधारण रूप से महत्वपूर्ण नहीं है, यह बहुत सारे गंदे switchबयानों या अन्य जांचों को बचाएगा !

संपादित करें

@ChaseMedallion को इंगित करने के लिए धन्यवाद कि मेरे मूल कार्यान्वयन ने नकारात्मक मूल्यों का समर्थन नहीं किया। यह उपलब्ध कराया गया है और परीक्षण किया गया है।

और इसे वापस करने के लिए परीक्षण:

[TestClass]
public class EnumExtensionsTests
{
    [Flags]
    enum WithFlags
    {
        First = 1,
        Second = 2,
        Third = 4,
        Fourth = 8
    }

    enum WithoutFlags
    {
        First = 1,
        Second = 22,
        Third = 55,
        Fourth = 13,
        Fifth = 127
    }

    enum WithoutNumbers
    {
        First, // 1
        Second, // 2
        Third, // 3
        Fourth // 4
    }

    enum WithoutFirstNumberAssigned
    {
        First = 7,
        Second, // 8
        Third, // 9
        Fourth // 10
    }


    enum WithNagativeNumbers
    {
        First = -7,
        Second = -8,
        Third = -9,
        Fourth = -10
    }

    [TestMethod]
    public void IsValidEnumTests()
    {
        Assert.IsTrue(((WithFlags)(1 | 4)).IsValid());
        Assert.IsTrue(((WithFlags)(1 | 4)).IsValid());
        Assert.IsTrue(((WithFlags)(1 | 4 | 2)).IsValid());
        Assert.IsTrue(((WithFlags)(2)).IsValid());
        Assert.IsTrue(((WithFlags)(3)).IsValid());
        Assert.IsTrue(((WithFlags)(1 + 2 + 4 + 8)).IsValid());

        Assert.IsFalse(((WithFlags)(16)).IsValid());
        Assert.IsFalse(((WithFlags)(17)).IsValid());
        Assert.IsFalse(((WithFlags)(18)).IsValid());
        Assert.IsFalse(((WithFlags)(0)).IsValid());

        Assert.IsTrue(((WithoutFlags)1).IsValid());
        Assert.IsTrue(((WithoutFlags)22).IsValid());
        Assert.IsTrue(((WithoutFlags)(53 | 6)).IsValid());   // Will end up being Third
        Assert.IsTrue(((WithoutFlags)(22 | 25 | 99)).IsValid()); // Will end up being Fifth
        Assert.IsTrue(((WithoutFlags)55).IsValid());
        Assert.IsTrue(((WithoutFlags)127).IsValid());

        Assert.IsFalse(((WithoutFlags)48).IsValid());
        Assert.IsFalse(((WithoutFlags)50).IsValid());
        Assert.IsFalse(((WithoutFlags)(1 | 22)).IsValid());
        Assert.IsFalse(((WithoutFlags)(9 | 27 | 4)).IsValid());

        Assert.IsTrue(((WithoutNumbers)0).IsValid());
        Assert.IsTrue(((WithoutNumbers)1).IsValid());
        Assert.IsTrue(((WithoutNumbers)2).IsValid());
        Assert.IsTrue(((WithoutNumbers)3).IsValid());
        Assert.IsTrue(((WithoutNumbers)(1 | 2)).IsValid()); // Will end up being Third
        Assert.IsTrue(((WithoutNumbers)(1 + 2)).IsValid()); // Will end up being Third

        Assert.IsFalse(((WithoutNumbers)4).IsValid());
        Assert.IsFalse(((WithoutNumbers)5).IsValid());
        Assert.IsFalse(((WithoutNumbers)25).IsValid());
        Assert.IsFalse(((WithoutNumbers)(1 + 2 + 3)).IsValid());

        Assert.IsTrue(((WithoutFirstNumberAssigned)7).IsValid());
        Assert.IsTrue(((WithoutFirstNumberAssigned)8).IsValid());
        Assert.IsTrue(((WithoutFirstNumberAssigned)9).IsValid());
        Assert.IsTrue(((WithoutFirstNumberAssigned)10).IsValid());

        Assert.IsFalse(((WithoutFirstNumberAssigned)11).IsValid());
        Assert.IsFalse(((WithoutFirstNumberAssigned)6).IsValid());
        Assert.IsFalse(((WithoutFirstNumberAssigned)(7 | 9)).IsValid());
        Assert.IsFalse(((WithoutFirstNumberAssigned)(8 + 10)).IsValid());

        Assert.IsTrue(((WithNagativeNumbers)(-7)).IsValid());
        Assert.IsTrue(((WithNagativeNumbers)(-8)).IsValid());
        Assert.IsTrue(((WithNagativeNumbers)(-9)).IsValid());
        Assert.IsTrue(((WithNagativeNumbers)(-10)).IsValid());
        Assert.IsFalse(((WithNagativeNumbers)(-11)).IsValid());
        Assert.IsFalse(((WithNagativeNumbers)(7)).IsValid());
        Assert.IsFalse(((WithNagativeNumbers)(8)).IsValid());
    }
}

1
इसके लिए धन्यवाद, मेरे पास एक समान मुद्दा था जो वैध ध्वज संयोजनों से संबंधित था। एनम के पहले चरित्र की जांच करने के विकल्प के रूप में, आप int.TryParse (enumValue.ToString ()) ... int करने की कोशिश भी कर सकते हैं ... यदि यह विफल रहता है, तो आपके पास झंडे का एक वैध सेट है। यह वास्तव में आपके समाधान की तुलना में धीमा हो सकता है, हालांकि।
MadHenchbot

यह कार्यान्वयन नकारात्मक मानों को सही ढंग से मान्य करने में विफल रहता है, क्योंकि चेक गैर-अंक वाले वर्णों के लिए है
चेसमैडेलियन

अच्छी पकड़!! मैं इस तरह के समायोजन के लिए अपना जवाब अपडेट करूंगा, धन्यवाद @ChaseMedallion
joshcomley

मुझे यह समाधान सबसे अच्छा लगता है, गणित के ट्रिक वाले केवल तभी काम करते हैं जब [Flags]समझदार पूर्णांक मान होते हैं।
MrLore

17

विहित उत्तर होगा Enum.IsDefined, लेकिन यह एक: थोड़ा धीमा है अगर एक तंग लूप में उपयोग किया जाता है, और बी: [Flags]एनम के लिए उपयोगी नहीं है ।

व्यक्तिगत रूप से, मैं इसके बारे में चिंता करना बंद कर दूंगा, और switchउचित रूप से, याद रखना:

  • अगर यह सब कुछ नहीं पहचानना ठीक है (और कुछ भी नहीं करना है), तो एक जोड़ नहीं default:(या एक खाली default:व्याख्या क्यों है)
  • अगर वहाँ एक समझदार डिफ़ॉल्ट व्यवहार है, कि में डाल दिया default:
  • अन्यथा, उन लोगों को संभालें जिनके बारे में आप जानते हैं और बाकी के लिए एक अपवाद फेंक दें:

इस तरह:

switch(someflag) {
    case TriBool.Yes:
        DoSomething();
        break;
    case TriBool.No:
        DoSomethingElse();
        break;
    case TriBool.FileNotFound:
        DoSomethingOther();
        break;
    default:
        throw new ArgumentOutOfRangeException("someflag");
}

[फ्लैग] एनम और प्रदर्शन से परिचित नहीं है, इसलिए आपका जवाब ऐसा नहीं है, जैसा कि इस कारण से लगता है कि एंथम का आविष्कार पहली जगह में क्यों किया गया था;) आपके "बिंदुओं" को देखते हुए या जो भी उन्हें बुलाया जाता है, उसके लिए आपको एक बिंदु होना चाहिए । शर्त लगा लो तुम उन्हें कुछ नहीं के लिए है, लेकिन लगता है कि वहाँ एक फ़ाइल में 257 मूल्यों की विन्यास फाइल पढ़ने की स्थिति के बारे में सोचो। अकेले दर्जनों अन्य दुश्मनी करते हैं। बहुत सारे केस-रेज़ होंगे ...
char m

@ मट्टी - जो एक चरम उदाहरण लगता है; deserialization वैसे भी एक विशेषज्ञ क्षेत्र है - अधिकांश क्रमांकन इंजन मुफ्त में एनम सत्यापन प्रदान करते हैं।
मार्क Gravell

@ माटी - एक साइड नोट पर; मैं कहूंगा कि उनके व्यक्तिगत गुणों के आधार पर उत्तर का इलाज करें। मुझे कभी-कभी चीजें पूरी तरह से गलत लगती हैं, और "प्रतिनिधि 17" के साथ कोई भी उतना ही सही जवाब दे सकता है।
मार्क Gravell

स्विच का उत्तर तेज है, लेकिन सामान्य नहीं है।
Eldritch Conundrum



4

[Flags]आप से निपटने के लिए सी # कुकबुक से इस समाधान का उपयोग कर सकते हैं :

सबसे पहले, ALLअपने एनम में एक नया मान जोड़ें :

[Flags]
enum Language
{
    CSharp = 1, VBNET = 2, VB6 = 4, 
    All = (CSharp | VBNET | VB6)
}

फिर, मान लें कि क्या है ALL:

public bool HandleFlagsEnum(Language language)
{
    if ((language & Language.All) == language)
    {
        return (true);
    }
    else
    {
        return (false);
    }
}

2

ऐसा करने का एक तरीका यह होगा कि आप कास्टिंग पर भरोसा करें और स्ट्रिंग रूपांतरण पर ध्यान दें। जब एक Enum टाइप करने के लिए int कास्टिंग int या तो एक समान एनम मान में बदल जाता है या परिणामी एनम सिर्फ एक मान के रूप में int शामिल है अगर एंम मूल्य int के लिए परिभाषित नहीं है।

enum NetworkStatus{
  Unknown=0,
  Active,
  Slow
}

int statusCode=2;
NetworkStatus netStatus = (NetworkStatus) statusCode;
bool isDefined = netStatus.ToString() != statusCode.ToString();

किसी भी किनारे के मामलों के लिए परीक्षण नहीं किया गया।


1

के रूप में अन्य लोगों ने कहा, Enum.IsDefinedरिटर्न falseभले ही आप थोड़ा झंडे के एक वैध संयोजन एक enum के साथ सजाया के लिए है FlagsAttribute

अफसोस की बात है, वैध बिट के झंडे के लिए सही तरीके से वापसी करने का एकमात्र तरीका थोड़ा लंबा है:

public static bool ValidateEnumValue<T>(T value) where T : Enum
{
    // Check if a simple value is defined in the enum.
    Type enumType = typeof(T);
    bool valid = Enum.IsDefined(enumType, value);
    // For enums decorated with the FlagsAttribute, allow sets of flags.
    if (!valid && enumType.GetCustomAttributes(typeof(FlagsAttribute), false)?.Any() == true)
    {
        long mask = 0;
        foreach (object definedValue in Enum.GetValues(enumType))
            mask |= Convert.ToInt64(definedValue);
        long longValue = Convert.ToInt64(value);
        valid = (mask & longValue) == longValue;
    }
    return valid;
}

आप GetCustomAttributeएक शब्दकोश के परिणामों को कैश करना चाह सकते हैं :

private static readonly Dictionary<Type, bool> _flagEnums = new Dictionary<Type, bool>();
public static bool ValidateEnumValue<T>(T value) where T : Enum
{
    // Check if a simple value is defined in the enum.
    Type enumType = typeof(T);
    bool valid = Enum.IsDefined(enumType, value);
    if (!valid)
    {
        // For enums decorated with the FlagsAttribute, allow sets of flags.
        if (!_flagEnums.TryGetValue(enumType, out bool isFlag))
        {
            isFlag = enumType.GetCustomAttributes(typeof(FlagsAttribute), false)?.Any() == true;
            _flagEnums.Add(enumType, isFlag);
        }
        if (isFlag)
        {
            long mask = 0;
            foreach (object definedValue in Enum.GetValues(enumType))
                mask |= Convert.ToInt64(definedValue);
            long longValue = Convert.ToInt64(value);
            valid = (mask & longValue) == longValue;
        }
    }
    return valid;
}

ध्यान दें कि ऊपर दिया गया कोड नए Enumअवरोध का उपयोग करता है Tजिस पर केवल C # 7.3 के बाद से उपलब्ध है। आपको object valueपुराने संस्करणों में पास होने और GetType()उस पर कॉल करने की आवश्यकता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.