วิธี TryParse สำหรับค่า Enum


98

ฉันต้องการเขียนฟังก์ชันที่สามารถตรวจสอบค่าที่กำหนด (ส่งผ่านเป็นสตริง) เทียบกับค่าที่เป็นไปได้ของenumไฟล์. ในกรณีของการจับคู่ควรส่งคืนอินสแตนซ์ enum มิฉะนั้นควรส่งคืนค่าเริ่มต้น

ฟังก์ชันต้องไม่ใช้ภายในtry/ catchซึ่งไม่รวมการใช้Enum.Parseซึ่งจะแสดงข้อยกเว้นเมื่อได้รับอาร์กิวเมนต์ที่ไม่ถูกต้อง

ฉันต้องการใช้บางอย่างตามบรรทัดของTryParseฟังก์ชันเพื่อใช้สิ่งนี้:

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
   object enumValue;
   if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
   {
       return defaultValue;
   }
   return (TEnum) enumValue;
}

8
ฉันไม่เข้าใจคำถามนี้ คุณกำลังพูดว่า "ฉันต้องการแก้ปัญหานี้ แต่ฉันไม่ต้องการใช้วิธีการใด ๆ ที่จะช่วยแก้ปัญหาให้ฉันได้" ประเด็นคืออะไร?
Domenic

1
คุณไม่ชอบลอง / จับวิธีแก้ปัญหาอะไร? หากคุณพยายามหลีกเลี่ยงข้อยกเว้นเนื่องจากมีค่าใช้จ่ายสูงโปรดหยุดพัก ใน 99% ของกรณีค่าใช้จ่ายในการโยน / รับข้อยกเว้นค่าใช้จ่ายมีน้อยมากเมื่อเทียบกับรหัสหลักของคุณ
SolutionYogi

4
@Domenic: ฉันแค่มองหาทางออกที่ดีกว่าสิ่งที่ฉันรู้อยู่แล้ว คุณเคยไปสอบถามทางรถไฟเพื่อขอเส้นทางหรือรถไฟที่คุณรู้จักอยู่แล้ว :)
Manish Basantani

2
@ โยกิ @ ธ อริน: ลอง ... การจับจะเป็นทางเลือกสุดท้ายของฉันเสมอ เกี่ยวกับการเสียค่าใช้จ่ายเราไม่เคยรู้ จะเกิดอะไรขึ้นถ้ามีคนเรียกวิธียูทิลิตี้ของฉันในรายการ 100 รายการ?
Manish Basantani

2
@Amby ค่าใช้จ่ายในการเข้าสู่บล็อก try / catch นั้นเล็กน้อย ค่าใช้จ่ายในการโยนข้อยกเว้นไม่ได้ แต่นั่นควรจะเป็นพิเศษใช่ไหม? นอกจากนี้อย่าพูดว่า "เราไม่เคยรู้" ... กำหนดรายละเอียดรหัสและค้นหา อย่าเสียเวลาสงสัยว่ามีอะไรช้าไปหา!
akmad

คำตอบ:


31

TryParseในฐานะที่เป็นคนอื่นได้ว่าคุณจะมีการดำเนินการของคุณเอง Simon Mourier นำเสนอการใช้งานเต็มรูปแบบซึ่งดูแลทุกอย่าง

หากคุณใช้ bitfield enums (เช่นแฟล็ก) คุณจะต้องจัดการกับสตริง"MyEnum.Val1|MyEnum.Val2"ซึ่งเป็นการรวมค่า enum สองค่าเข้าด้วยกัน ถ้าคุณเรียกEnum.IsDefinedด้วยสตริงนี้มันจะส่งคืนเท็จแม้ว่าจะEnum.Parseจัดการอย่างถูกต้อง

อัปเดต

ดังที่ลิซ่าและคริสเตียนกล่าวไว้ในความคิดเห็นEnum.TryParseตอนนี้พร้อมใช้งานสำหรับ C # ใน. NET4 ขึ้นไป

MSDN Docs


อาจจะเซ็กซี่น้อยที่สุด แต่ฉันยอมรับว่านี่เป็นสิ่งที่ดีที่สุดจนกว่ารหัสของคุณจะถูกย้ายไปที่. NET 4
Lisa

1
ดังที่กล่าวไว้ด้านล่าง แต่ไม่สามารถมองเห็นได้จริง: ณ . Net 4 Enum มี TryParse และทำงานได้โดยไม่ต้องเข้ารหัส ดูข้อมูลเพิ่มเติมได้จาก MSDN: msdn.microsoft.com/library/vstudio/dd991317%28v=vs.100%29.aspx
Christian

107

Enum.IsDefined จะทำสิ่งต่างๆให้ลุล่วง อาจไม่มีประสิทธิภาพเท่า TryParse แต่จะทำงานได้โดยไม่มีข้อยกเว้นในการจัดการ

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
    if (!Enum.IsDefined(typeof(TEnum), strEnumValue))
        return defaultValue;

    return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue);
}

สิ่งที่น่าสังเกต: มีTryParseการเพิ่มวิธีการใน. NET 4.0


1
คำตอบที่ดีที่สุดที่ฉันเคยเห็น ... ไม่ลอง / จับไม่มี GetNames :)
Thomas Levesque

13
ข้อเสียของ Enum.IsDefined: blogs.msdn.com/brada/archive/2003/11/29/50903.aspx
Nader Shirazie

6
นอกจากนี้ยังไม่มีกรณีที่เพิกเฉยใน IsDefined
Anthony Johnston

2
@Anthony: GetNamesถ้าคุณต้องการที่จะสนับสนุนกรณีที่ไม่รู้สึกคุณจะต้อง ภายในวิธีการทั้งหมดเหล่านี้ (รวมถึงParse) ใช้GetHashEntryซึ่งจะทำการสะท้อนจริง - ครั้งเดียว ในด้านสว่าง. NET 4.0 มี TryParse และมันก็ทั่วไปเช่นกัน :)
Thorarin

+1 มันช่วยวันของฉัน! ฉันกำลังสำรองรหัสจำนวนมากจาก. NET 4 เป็น. NET 3.5 และคุณช่วยฉันไว้ :)
daitangio

20

นี่คือการใช้งานที่กำหนดเองของEnumTryParse. ซึ่งแตกต่างจากการใช้งานทั่วไปอื่น ๆ แต่ยังสนับสนุน enum ที่มีFlagsแอตทริบิวต์ด้วย

    /// <summary>
    /// Converts the string representation of an enum to its Enum equivalent value. A return value indicates whether the operation succeeded.
    /// This method does not rely on Enum.Parse and therefore will never raise any first or second chance exception.
    /// </summary>
    /// <param name="type">The enum target type. May not be null.</param>
    /// <param name="input">The input text. May be null.</param>
    /// <param name="value">When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded.</param>
    /// <returns>
    /// true if s was converted successfully; otherwise, false.
    /// </returns>
    public static bool EnumTryParse(Type type, string input, out object value)
    {
        if (type == null)
            throw new ArgumentNullException("type");

        if (!type.IsEnum)
            throw new ArgumentException(null, "type");

        if (input == null)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        input = input.Trim();
        if (input.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        string[] names = Enum.GetNames(type);
        if (names.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        Type underlyingType = Enum.GetUnderlyingType(type);
        Array values = Enum.GetValues(type);
        // some enums like System.CodeDom.MemberAttributes *are* flags but are not declared with Flags...
        if ((!type.IsDefined(typeof(FlagsAttribute), true)) && (input.IndexOfAny(_enumSeperators) < 0))
            return EnumToObject(type, underlyingType, names, values, input, out value);

        // multi value enum
        string[] tokens = input.Split(_enumSeperators, StringSplitOptions.RemoveEmptyEntries);
        if (tokens.Length == 0)
        {
            value = Activator.CreateInstance(type);
            return false;
        }

        ulong ul = 0;
        foreach (string tok in tokens)
        {
            string token = tok.Trim(); // NOTE: we don't consider empty tokens as errors
            if (token.Length == 0)
                continue;

            object tokenValue;
            if (!EnumToObject(type, underlyingType, names, values, token, out tokenValue))
            {
                value = Activator.CreateInstance(type);
                return false;
            }

            ulong tokenUl;
            switch (Convert.GetTypeCode(tokenValue))
            {
                case TypeCode.Int16:
                case TypeCode.Int32:
                case TypeCode.Int64:
                case TypeCode.SByte:
                    tokenUl = (ulong)Convert.ToInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;

                //case TypeCode.Byte:
                //case TypeCode.UInt16:
                //case TypeCode.UInt32:
                //case TypeCode.UInt64:
                default:
                    tokenUl = Convert.ToUInt64(tokenValue, CultureInfo.InvariantCulture);
                    break;
            }

            ul |= tokenUl;
        }
        value = Enum.ToObject(type, ul);
        return true;
    }

    private static char[] _enumSeperators = new char[] { ',', ';', '+', '|', ' ' };

    private static object EnumToObject(Type underlyingType, string input)
    {
        if (underlyingType == typeof(int))
        {
            int s;
            if (int.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(uint))
        {
            uint s;
            if (uint.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ulong))
        {
            ulong s;
            if (ulong.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(long))
        {
            long s;
            if (long.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(short))
        {
            short s;
            if (short.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(ushort))
        {
            ushort s;
            if (ushort.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(byte))
        {
            byte s;
            if (byte.TryParse(input, out s))
                return s;
        }

        if (underlyingType == typeof(sbyte))
        {
            sbyte s;
            if (sbyte.TryParse(input, out s))
                return s;
        }

        return null;
    }

    private static bool EnumToObject(Type type, Type underlyingType, string[] names, Array values, string input, out object value)
    {
        for (int i = 0; i < names.Length; i++)
        {
            if (string.Compare(names[i], input, StringComparison.OrdinalIgnoreCase) == 0)
            {
                value = values.GetValue(i);
                return true;
            }
        }

        if ((char.IsDigit(input[0]) || (input[0] == '-')) || (input[0] == '+'))
        {
            object obj = EnumToObject(underlyingType, input);
            if (obj == null)
            {
                value = Activator.CreateInstance(type);
                return false;
            }
            value = obj;
            return true;
        }

        value = Activator.CreateInstance(type);
        return false;
    }

1
คุณให้การใช้งานที่ดีที่สุดและฉันได้ใช้เพื่อวัตถุประสงค์ของฉันเอง แต่ผมกำลังสงสัยว่าทำไมคุณใช้Activator.CreateInstance(type)เพื่อสร้างมูลค่า enum Enum.ToObject(type, 0)เริ่มต้นและไม่ แค่เรื่องของรสชาติ?
Pierre Arnaud

1
@ ปิแอร์ - อืมม ... ไม่มันดูเป็นธรรมชาติมากขึ้นในตอนนั้น :-) บางที Enum ToObject เร็วกว่าเนื่องจากใช้การโทรภายใน InternalBoxEnum? ฉันไม่เคยตรวจสอบว่า ...
Simon Mourier

2
ดังที่กล่าวไว้ด้านล่าง แต่ไม่สามารถมองเห็นได้จริง: ณ . Net 4 Enum TryParse พร้อมใช้งานและทำงานได้โดยไม่ต้องเข้ารหัสเพิ่มเติม ดูข้อมูลเพิ่มเติมได้จาก MSDN: msdn.microsoft.com/library/vstudio/dd991317%28v=vs.100%29.aspx
Christian

16

ในท้ายที่สุดคุณต้องใช้สิ่งนี้Enum.GetNames:

public bool TryParseEnum<T>(string str, bool caseSensitive, out T value) where T : struct {
    // Can't make this a type constraint...
    if (!typeof(T).IsEnum) {
        throw new ArgumentException("Type parameter must be an enum");
    }
    var names = Enum.GetNames(typeof(T));
    value = (Enum.GetValues(typeof(T)) as T[])[0];  // For want of a better default
    foreach (var name in names) {
        if (String.Equals(name, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) {
            value = (T)Enum.Parse(typeof(T), name);
            return true;
        }
    }
    return false;
}

หมายเหตุเพิ่มเติม:

  • Enum.TryParseรวมอยู่ใน. NET 4 ดูที่นี่http://msdn.microsoft.com/library/dd991876(VS.100).aspx
  • อีกวิธีหนึ่งคือการEnum.Parseจับข้อยกเว้นที่โยนทิ้งเมื่อล้มเหลวโดยตรง ซึ่งอาจเร็วกว่าเมื่อพบการแข่งขัน แต่มีแนวโน้มที่จะช้าลงหากไม่ ขึ้นอยู่กับข้อมูลที่คุณกำลังประมวลผลซึ่งอาจเป็นการปรับปรุงสุทธิหรือไม่ก็ได้

แก้ไข: เพิ่งเห็นการใช้งานที่ดีขึ้นซึ่งเก็บข้อมูลที่จำเป็นไว้: http://damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance-and-tryparse-in-net- 3-5


ฉันจะแนะนำให้ใช้ค่าเริ่มต้น (T) เพื่อตั้งค่าเริ่มต้น ปรากฎว่าสิ่งนี้ใช้ไม่ได้กับ enums ทั้งหมด เช่นถ้าประเภทพื้นฐานสำหรับ enum เป็น int default (T) จะคืนค่า 0 เสมอซึ่งอาจจะใช้ได้หรือไม่ก็ได้สำหรับ enum
Daniel Ballinger

การใช้งานที่บล็อกของ Damieng ไม่สนับสนุน enums ที่มีFlagsแอตทริบิวต์
Uwe Keim

9

อ้างอิงจาก. NET 4.5

โค้ดตัวอย่างด้านล่าง

using System;

enum Importance
{
    None,
    Low,
    Medium,
    Critical
}

class Program
{
    static void Main()
    {
    // The input value.
    string value = "Medium";

    // An unitialized variable.
    Importance importance;

    // Call Enum.TryParse method.
    if (Enum.TryParse(value, out importance))
    {
        // We now have an enum type.
        Console.WriteLine(importance == Importance.Medium);
    }
    }
}

อ้างอิง: http://www.dotnetperls.com/enum-parse


4

ฉันมีการดำเนินการที่ดีที่สุดที่คุณสามารถใช้ในการUnconstrainedMelody มันเป็นเพียงการแคชรายชื่ออย่างมีประสิทธิภาพ แต่มันทำได้ดีพิมพ์มากและ จำกัด โดยทั่วไป :)


4
enum EnumStatus
{
    NAO_INFORMADO = 0,
    ENCONTRADO = 1,
    BLOQUEADA_PELO_ENTREGADOR = 2,
    DISPOSITIVO_DESABILITADO = 3,
    ERRO_INTERNO = 4,
    AGARDANDO = 5
}

...

if (Enum.TryParse<EnumStatus>(item.status, out status)) {

}

2

ขณะนี้ยังไม่มี Enum ออกจากกล่อง TryParse สิ่งนี้ได้รับการร้องขอบน Connect ( ยังไม่มี Enum.TryParse ) และได้รับการตอบสนองที่ระบุว่าอาจรวมอยู่ในกรอบงานถัดไปหลังจาก. NET 3.5 คุณจะต้องใช้วิธีแก้ปัญหาที่แนะนำในตอนนี้


1

วิธีเดียวที่จะหลีกเลี่ยงการจัดการข้อยกเว้นคือการใช้เมธอด GetNames () และเราทุกคนรู้ดีว่าไม่ควรใช้ข้อยกเว้นสำหรับตรรกะแอปพลิเคชันทั่วไป :)


1
มันไม่ได้เป็นเพียงวิธี Enum.IsDefined (.. ) จะป้องกันการโยนข้อยกเว้นในรหัสผู้ใช้
Thorarin

1

การแคชฟังก์ชัน / พจนานุกรมที่สร้างขึ้นแบบไดนามิกอนุญาตหรือไม่

เนื่องจากคุณไม่ (ดูเหมือน) ทราบประเภทของ enum ล่วงหน้าการดำเนินการครั้งแรกอาจทำให้เกิดการดำเนินการที่ตามมาจึงอาจใช้ประโยชน์ได้

คุณสามารถแคชผลลัพธ์ของ Enum.GetNames ()

คุณกำลังพยายามเพิ่มประสิทธิภาพสำหรับ CPU หรือหน่วยความจำ? คุณต้องการจริงๆหรือ?


แนวคิดคือการเพิ่มประสิทธิภาพ CPU ยอมรับว่าฉันสามารถทำได้โดยใช้หน่วยความจำต้นทุน แต่มันไม่ใช่วิธีแก้ปัญหาที่ฉันกำลังมองหา ขอบคุณ.
Manish Basantani

0

อย่างที่คนอื่นพูดไปแล้วถ้าคุณไม่ได้ใช้ Try & Catch คุณต้องใช้ IsDefined หรือ GetNames ... นี่คือตัวอย่างบางส่วน ... โดยพื้นฐานแล้วจะเหมือนกันทั้งหมดอันแรกที่จัดการ enums ที่เป็นโมฆะ ฉันชอบอันที่ 2 เพราะเป็นส่วนขยายของสตริงไม่ใช่ enums ... แต่คุณสามารถผสมได้ตามที่คุณต้องการ!

  • www.objectreference.net/post/Enum-TryParse-Extension-Method.aspx
  • flatlinerdoa.spaces.live.com/blog/cns!17124D03A9A052B0!605.entry
  • mironabramson.com/blog/post/2008/03/Another-version-for-the-missing-method-EnumTryParse.aspx
  • lazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html

0

ไม่มี TryParse เนื่องจากไม่รู้จักประเภทของ Enum จนกว่าจะรันไทม์ TryParse ที่เป็นไปตามวิธีการเดียวกับที่บอกว่าเมธอด Date.TryParse จะทำให้เกิดข้อผิดพลาดในการแปลงโดยปริยายบนพารามิเตอร์ ByRef

ฉันขอแนะนำให้ทำสิ่งนี้:

//1 line call to get value
MyEnums enumValue = (Sections)EnumValue(typeof(Sections), myEnumTextValue, MyEnums.SomeEnumDefault);

//Put this somewhere where you can reuse
public static object EnumValue(System.Type enumType, string value, object NotDefinedReplacement)
{
    if (Enum.IsDefined(enumType, value)) {
        return Enum.Parse(enumType, value);
    } else {
        return Enum.Parse(enumType, NotDefinedReplacement);
    }
}

สำหรับTryเมธอดที่ผลลัพธ์อาจเป็นชนิดค่าหรือที่ซึ่งnullอาจเป็นผลลัพธ์ที่ถูกต้อง (เช่นDictionary.TryGetValue, which has both such traits), the normal pattern is for a วิธีการลอง "เพื่อส่งคืนboolและส่งผลลัพธ์เป็นoutพารามิเตอร์สำหรับวิธีการที่ส่งคืนประเภทคลาสที่nullไม่ใช่ผลลัพธ์ที่ถูกต้องจะไม่มีปัญหาในการnullส่งคืน เพื่อบ่งบอกถึงความล้มเหลว
supercat

-1

ลองดูที่คลาส Enum (โครงสร้าง?) มีวิธีแยกวิเคราะห์อยู่ แต่ฉันไม่แน่ใจเกี่ยวกับ tryparse


ฉันรู้เกี่ยวกับวิธี Enum.Parse (typeof (TEnum), strEnumValue) มันพ่น ArgumentException ถ้า strEnumValue ไม่ถูกต้อง กำลังมองหา TryParse ........
Manish Basantani

-2

วิธีนี้จะแปลงประเภทของ enum:

  public static TEnum ToEnum<TEnum>(object EnumValue, TEnum defaultValue)
    {
        if (!Enum.IsDefined(typeof(TEnum), EnumValue))
        {
            Type enumType = Enum.GetUnderlyingType(typeof(TEnum));
            if ( EnumValue.GetType() == enumType )
            {
                string name = Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue);
                if( name != null)
                    return (TEnum)Enum.Parse(typeof(TEnum), name);
                return defaultValue;
            }
        }
        return (TEnum)Enum.Parse(typeof(TEnum), EnumValue.ToString());
    } 

จะตรวจสอบประเภทพื้นฐานและรับชื่อเพื่อแยกวิเคราะห์ หากทุกอย่างล้มเหลวจะคืนค่าเริ่มต้น


3
สิ่งนี้กำลังทำอะไร "Enum.GetName (typeof (HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue)" อาจจะขึ้นอยู่กับรหัสในเครื่องของคุณ
Manish Basantani
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.