Enum値のTryParseの方法


94

特定の値(文字列として渡される)をの可能な値に対して検証できる関数を書きたいのですが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

1
例外処理のコストはそれほど悪くありません。地獄、このすべての列挙型変換の内部実装は、例外処理でいっぱいです。ただし、通常のアプリケーションロジック中に例外がスローおよびキャッチされるのは本当に嫌いです。スローされているすべての例外を(ブレークがキャッチされている場合でも)中断すると便利な場合があります。どこにでも例外を投げると、それを使うのはもっと面倒になります:)
Thorarin

3
@Domenic:私はすでに知っているよりも良い解決策を探しています。あなたはすでに知っているルートや電車を尋ねるために鉄道の問い合わせに行きますか:)。
Manish Basantani 2009

2
@アンビー、try / catchブロックに入るだけのコストはごくわずかです。例外をスローするコストはありませんが、それは例外的であるはずですよね?また、「わかりません」とは言わないでください...コードのプロファイルを作成して調べてください。何かが遅いのではないかと思って時間を無駄にしないでください。
akmad 2009

回答:


31

他の人が言ったように、あなた自身のものを実装しなければなりませんTryParse。Simon Mourierは、すべてを処理する完全な実装を提供しています。

ビットフィールド列挙型(つまりフラグ)を使用している場合"MyEnum.Val1|MyEnum.Val2"は、2つの列挙型値を組み合わせたような文字列も処理する必要があります。Enum.IsDefinedこの文字列で呼び出すと、Enum.Parse正しく処理されてもfalseが返されます。

更新

コメントでリサとクリスチャンが述べたようにEnum.TryParse、.NET4以降のC#で利用できるようになりました。

MSDNドキュメント


おそらく最もセクシーな、しかし、私はあなたのコードは、.NET 4に移行されるまで、これは間違いなく最高であることに同意
リサ

1
以下で説明しますが、実際には表示されません。詳細については、MSDNから入手できます:msdn.microsoft.com/library/vstudio/dd991317%28v=vs.100%29.aspx
Christian

106

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
これまで見てきたベストアンサー... try / catchなし、GetNamesなし:)
Thomas Levesque


6
また、IsDefinedの無視ケースはありません
アンソニージョンストン

2
@Anthony:大文字と小文字を区別しない場合は、が必要になりますGetNames。内部的には、これらのすべてのメソッド(を含むParse)はを使用しますGetHashEntry。明るい面として、.NET 4.0にはTryParseがあり、それも汎用的です:)
Thorarin

+1それは私の日を救った!
一連

20

これはのカスタム実装ですEnumTryParse。他の一般的な実装とは異なり、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.ToObject(type, 0)ます。好みの問題?
Pierre Arnaud

1
@Pierre-うーん...いいえ、そのときはより自然に見えました:-)内部呼び出しInternalBoxEnumを内部的に使用しているため、Enum.ToObjectの方が高速かもしれません。私はそれをチェックしたことはありません...
Simon Mourier '13

2
以下で説明しますが、実際には表示されません。詳細については、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


default(T)を使用してデフォルト値を設定することをお勧めします。これはすべての列挙型では機能しないことがわかりました。たとえば、列挙型の基礎となる型がintだった場合、default(T)は常に0を返します。これは、列挙型に対して有効な場合と無効な場合があります。
Daniel Ballinger、

Damiengのブログの実装では、属性を持つ列挙型サポートされていませFlags
Uwe Keim 2013

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(..)は、ユーザーコードで例外がスローされるのを防ぎます。
ソラリン

1

動的に生成された関数/辞書のキャッシングは許可されますか?

列挙型のタイプが事前にわからない(見えない)ため、最初の実行では、後続の実行で利用できるものを生成できます。

Enum.GetNames()の結果をキャッシュすることもできます

CPUまたはメモリを最適化しようとしていますか?あなたは本当にする必要がありますか?


アイデアはCPUを最適化することです。私はコストメモリでそれを行うことができることに同意します。しかし、それは私が探している解決策ではありません。ありがとう。
Manish Basantani 09

0

他の人がすでに言ったように、Try&Catchを使用しない場合は、IsDefinedまたはGetNamesを使用する必要があります...以下にいくつかのサンプルを示します。列挙型ではなく文字列の拡張であるため、私は2番目の方を好みますが、必要に応じてそれらを混在させることができます!

  • 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

Enumの型は実行時までわからないため、TryParseはありません。Date.TryParseメソッドと同じ方法に従う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 Try`方法を返すようにbool、そしてその結果を渡すoutパラメータ。いるものについてはリターン・クラス・タイプここでは、null有効な結果ではないが、使用して支障がないnullリターン失敗を示すため
supercat

-1

Enumクラス(struct?)自体を見てください。その上にParseメソッドがありますが、tryparseについてはわかりません。


Enum.Parse(typeof(TEnum)、strEnumValue)メソッドについて知っています。strEnumValueが有効でない場合は、ArgumentExceptionをスローします。TryParseを探しています........
Manish Basantani 09

-2

このメソッドは列挙型を変換します:

  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、2010年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.