C#で文字列を列挙型に変換する


894

C#で文字列を列挙値に変換する最良の方法は何ですか?

列挙型の値を含むHTML selectタグがあります。ページが投稿されたら、値(文字列の形式になります)を取得し、それを列挙値に変換します。

理想的な世界では、次のようなことができます。

StatusEnum MyStatus = StatusEnum.Parse("Active");

しかし、それは有効なコードではありません。

回答:


1510

.NET Coreおよび.NET> 4 には、汎用の解析メソッドがあります。

Enum.TryParse("Active", out StatusEnum myStatus);

これには、C#7の新しいインラインout変数も含まれるため、これはtry-parse、明示的な列挙型への変換、およびmyStatus変数の初期化と入力を行います。

C#7と最新の.NETにアクセスできる場合、これが最善の方法です。

元の回答

.NETでは、それは(4以上になるまで)かなり醜いです:

StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum), "Active", true);

私はこれを簡単にする傾向があります:

public static T ParseEnum<T>(string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

その後、私は行うことができます:

StatusEnum MyStatus = EnumUtil.ParseEnum<StatusEnum>("Active");

コメントで提案されている1つのオプションは、十分に簡単な拡張機能を追加することです。

public static T ToEnum<T>(this string value)
{
    return (T) Enum.Parse(typeof(T), value, true);
}

StatusEnum MyStatus = "Active".ToEnum<StatusEnum>();

最後に、文字列を解析できない場合に使用するデフォルトの列挙型が必要になる場合があります。

public static T ToEnum<T>(this string value, T defaultValue) 
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    T result;
    return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}

これはこれを呼び出しにします:

StatusEnum MyStatus = "Active".ToEnum(StatusEnum.None);

ただし、このような拡張メソッドstringを(名前空間の制御なしで)stringenumを保持するかどうかのすべてのインスタンスに表示されるように(1234.ToString().ToEnum(StatusEnum.None)有効ですが無意味です)ように注意して追加します。多くの場合、開発チーム全体がこれらの拡張機能の機能を十分に理解していない限り、非常に特定のコンテキストにのみ適用される追加のメソッドでMicrosoftのコアクラスが乱雑にならないようにするのが最善です。


17
下のMckenzieg1によって与えられる性能もが重要な場合(これは常に)CHKの答え:stackoverflow.com/questions/16100/...
ナッシュ

28
@avinashrは@ McKenzieG1の答えについては正しいですが、常に重要ではありません。たとえば、解析ごとにDB呼び出しを行っている場合、列挙型の解析を心配するのは無意味なマイクロ最適化です。
キース、

4
@HMここでは拡張子は適切ではないと思います。これは少し特殊なケースであり、拡張子はすべての文字列に適用さます。あなたが本当にそれをやりたいのなら、それは些細な変化でしょう。
キース

7
Enum.TryParseはどうですか?
Elaine

15
非常に素晴らしい。最後の例ではwhere T:structが必要です。
bbrik

331

使用Enum.TryParse<T>(String, T)(≥.NET 4.0):

StatusEnum myStatus;
Enum.TryParse("Active", out myStatus);

C#7.0のパラメータータイプのインライン化により、さらに簡素化できます。

Enum.TryParse("Active", out StatusEnum myStatus);

45
大文字と小文字を区別するために真ん中のブール値パラメーターを追加します。これは、最も安全で最もエレガントなソリューションです。
DanM7 2014

18
さあ、下にスクロールして、これがより良い(モダンな)答えであることを見つけるために、2008年からその選択された答えを実装した人の数。
TEK 2016年

@TEK 2008年の回答を実際に好む。
Zero3 '11

わかりません。Parse変換で問題が発生した場合(値がnull、空、または対応する列挙定数がない場合)の説明的な例外をスローします。これは、TryParseブール値の戻り値(具体的なエラーを抑制する)よりも優れています
yair

2
Enum.TryParse <T>(String、T)は、整数文字列を解析するときに欠陥があります。たとえば、このコードが正常に無意味な列挙として無意味な文字列を解析します: var result = Enum.TryParse<System.DayOfWeek>("55", out var parsedEnum);
マスドットネット

196

Enum.Parse()リフレクションを介して実装されるため、パフォーマンスはひどいことに注意してください。(同じEnum.ToStringことが、反対方向にも当てはまります)にも当てはまります。

パフォーマンスに敏感なコードで文字列をEnumに変換する必要がある場合はDictionary<String,YourEnum>、起動時にat を作成し、それを使用して変換を行うのが最善の方法です。


10
デスクトップコンピューターで、最初の実行時に文字列をEnumに変換するために3ミリ秒を測定しました。(ちょうどawfullnessのレベルを示すため)。
Matthieu Charbonnier

12
すごい3ミリ秒はひどい規模のオーダーです
ジョン株式

1
これの周りにコードサンプルを追加できますか?それで、置換および使用方法に関するアイデアを得ます
トランスフォーマー

アプリを100万人が使用している場合=>これにより、消費する人間の生活が最大50時間増加します:) 1ページでの使用。:P
カタリンRădoi


31

ここで拡張メソッドを使用できます。

public static T ToEnum<T>(this string value, bool ignoreCase = true)
{
    return (T) Enum.Parse(typeof (T), value, ignoreCase);
}

そして、あなたは以下のコードでそれらを呼び出すことができます(ここでFilterTypeは、列挙型です):

FilterType filterType = type.ToEnum<FilterType>();

1
これを更新して、オブジェクトとして値を取得し、このメソッド内で文字列にキャストしました。このようにして、文字列だけではなくint値.ToEnumを取ることができます。
RealSollyM

2
@SollyM私はそれが恐ろしいアイデアの原因だと思います。その場合、この拡張メソッドはすべてのオブジェクトタイプに適用されます。私の意見では、stringとintの2つの拡張メソッドは、よりクリーンではるかに安全です。
2014

@Svish、それは本当です。私がこれを行った唯一の理由は、コードが内部でのみ使用され、2つの拡張機能の作成を避けたかったためです。また、Enumに変換するのは文字列またはintのみなので、それ以外の場合は問題になりませんでした。
RealSollyM 2014

3
@SollyM内部かどうか、私はまだ私のコードを維持して使用しています:すべてのインテリセンスメニューでToEnumを表示すると、PIは煩わしいでしょうまたはint、あなたはあなたがこれらの2つのメソッドだけを必要とすることをかなり確信することができます。また、2つの方法はそれほど多くはありません。特に、これらが非常に小さく、ユーティリティタイプである場合:P
Svish

20
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);

したがって、moodという名前の列挙型がある場合、次のようになります。

   enum Mood
   {
      Angry,
      Happy,
      Sad
   } 

   // ...
   Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
   Console.WriteLine("My mood is: {0}", m.ToString());

18

注意:

enum Example
{
    One = 1,
    Two = 2,
    Three = 3
}

Enum.(Try)Parse() 複数のコンマ区切りの引数を受け入れ、それらをバイナリの 'or'と組み合わせます|。あなたはこれを無効にすることはできませんし、私の意見ではほとんどそれを望んでいません。

var x = Enum.Parse("One,Two"); // x is now Three

Three定義されていない場合xでも、int値を取得します3。さらに悪いことです。Enum.Parse()は、列挙型に対しても定義されていない値を与える可能性があります。

私は、ユーザーの結果を喜んでまたは望まずに体験して、この動作を引き起こしたくありません。

さらに、他の人が述べたように、パフォーマンスは大きな列挙型にとって理想的ではありません。つまり、可能な値の数が線形です。

私は以下を提案します:

    public static bool TryParse<T>(string value, out T result)
        where T : struct
    {
        var cacheKey = "Enum_" + typeof(T).FullName;

        // [Use MemoryCache to retrieve or create&store a dictionary for this enum, permanently or temporarily.
        // [Implementation off-topic.]
        var enumDictionary = CacheHelper.GetCacheItem(cacheKey, CreateEnumDictionary<T>, EnumCacheExpiration);

        return enumDictionary.TryGetValue(value.Trim(), out result);
    }

    private static Dictionary<string, T> CreateEnumDictionary<T>()
    {
        return Enum.GetValues(typeof(T))
            .Cast<T>()
            .ToDictionary(value => value.ToString(), value => value, StringComparer.OrdinalIgnoreCase);
    }

4
実際、これはそれを知るのに非常に役立ちEnum.(Try)Parse accepts multiple, comma-separated arguments, and combines them with binary 'or'ます。列挙値を2の累乗として設定でき、複数のブールフラグを解析する非常に簡単な方法があることを意味します。「UseSSL、NoRetries、Sync」。実際、それはおそらくそれがそのために設計されたものです。
pcdev 2018


13

受け入れられた回答をデフォルト値で拡張して、例外を回避できます。

public static T ParseEnum<T>(string value, T defaultValue) where T : struct
{
    try
    {
        T enumValue;
        if (!Enum.TryParse(value, true, out enumValue))
        {
            return defaultValue;
        }
        return enumValue;
    }
    catch (Exception)
    {
        return defaultValue;
    }
}

次に、次のように呼び出します。

StatusEnum MyStatus = EnumUtil.ParseEnum("Active", StatusEnum.None);

デフォルト値が列挙型でない場合、Enum.TryParseは失敗し、キャッチされる例外をスローします。

多くの場所でコードでこの関数を何年も使用した後、この操作にはパフォーマンスが必要であることを示す情報を追加するとよいでしょう。


デフォルト値は好きではありません。予期しない結果が生じる可能性があります。
ダニエル・チューリップ

5
これはいつ例外をスローしますか?
andleer 2016年

列挙値がデフォルト値と同じ列挙型に適合しない場合は@andleer
Nelly

@Nelly古いコードはここにありますが、defaultValueおよびメソッドの戻り値の型は両方ともですT。タイプが異なる場合は、コンパイル時エラー「「ConsoleApp1.Size」から「ConsoleApp1.Color」に変換できません」などのタイプが返されます。
andleer

@andleer、あなたへの私の最後の答えが正しくなかったのは残念です。列挙型ではないデフォルト値で誰かがこの関数を呼び出した場合、このメソッドがSyste.ArgumentExceptionをスローする可能性があります。c#7.0では、T:Enumのwhere句を作成できませんでした。トライキャッチでこの可能性を見つけたのはそのためです。
ネリー

8

完全に有効な入力を想定することはできず、@ Keithの回答のこのバリエーションを使いました。

public static TEnum ParseEnum<TEnum>(string value) where TEnum : struct
{
    TEnum tmp; 
    if (!Enum.TryParse<TEnum>(value, true, out tmp))
    {
        tmp = new TEnum();
    }
    return tmp;
}


5

.NET 4.5のtry / catchおよびTryParse()メソッドなしで文字列をTEnumに解析します。

/// <summary>
/// Parses string to TEnum without try/catch and .NET 4.5 TryParse()
/// </summary>
public static bool TryParseToEnum<TEnum>(string probablyEnumAsString_, out TEnum enumValue_) where TEnum : struct
{
    enumValue_ = (TEnum)Enum.GetValues(typeof(TEnum)).GetValue(0);
    if(!Enum.IsDefined(typeof(TEnum), probablyEnumAsString_))
        return false;

    enumValue_ = (TEnum) Enum.Parse(typeof(TEnum), probablyEnumAsString_);
    return true;
}

1
コードにすでに説明が含まれている場合、説明を作成する必要があるかどうか。
わかりまし

3

TryParseを使用した非常にシンプルなコード:

var value = "Active";

StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
    status = StatusEnum.Unknown;

2

拡張メソッドソリューションが好きです。

namespace System
{
    public static class StringExtensions
    {

        public static bool TryParseAsEnum<T>(this string value, out T output) where T : struct
        {
            T result;

            var isEnum = Enum.TryParse(value, out result);

            output = isEnum ? result : default(T);

            return isEnum;
        }
    }
}

以下は、テストによる私の実装です。

using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
using static System.Console;

private enum Countries
    {
        NorthAmerica,
        Europe,
        Rusia,
        Brasil,
        China,
        Asia,
        Australia
    }

   [TestMethod]
        public void StringExtensions_On_TryParseAsEnum()
        {
            var countryName = "Rusia";

            Countries country;
            var isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsTrue(isCountry);
            AreEqual(Countries.Rusia, country);

            countryName = "Don't exist";

            isCountry = countryName.TryParseAsEnum(out country);

            WriteLine(country);

            IsFalse(isCountry);
            AreEqual(Countries.NorthAmerica, country); // the 1rst one in the enumeration
        }

1
public static T ParseEnum<T>(string value)            //function declaration  
{
    return (T) Enum.Parse(typeof(T), value);
}

Importance imp = EnumUtil.ParseEnum<Importance>("Active");   //function call

====================完全なプログラム====================

using System;

class Program
{
    enum PetType
    {
    None,
    Cat = 1,
    Dog = 2
    }

    static void Main()
    {

    // Possible user input:
    string value = "Dog";

    // Try to convert the string to an enum:
    PetType pet = (PetType)Enum.Parse(typeof(PetType), value);

    // See if the conversion succeeded:
    if (pet == PetType.Dog)
    {
        Console.WriteLine("Equals dog.");
    }
    }
}
-------------
Output

Equals dog.

1

私はクラスを使用しました(構文解析とパフォーマンスの改善を伴うEnumの厳密に型指定されたバージョン)。GitHubで見つけました。.NET3.5でも動作するはずです。辞書をバッファリングするため、メモリのオーバーヘッドが多少あります。

StatusEnum MyStatus = Enum<StatusEnum>.Parse("Active");

ブログ投稿はEnumsです– NET 3.5での構文の改善、パフォーマンスの向上、TryParse

そしてコード:https : //github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs


1

パフォーマンスのために、これは役立つかもしれません:

    private static Dictionary<Type, Dictionary<string, object>> dicEnum = new Dictionary<Type, Dictionary<string, object>>();
    public static T ToEnum<T>(this string value, T defaultValue)
    {
        var t = typeof(T);
        Dictionary<string, object> dic;
        if (!dicEnum.ContainsKey(t))
        {
            dic = new Dictionary<string, object>();
            dicEnum.Add(t, dic);
            foreach (var en in Enum.GetValues(t))
                dic.Add(en.ToString(), en);
        }
        else
            dic = dicEnum[t];
        if (!dic.ContainsKey(value))
            return defaultValue;
        else
            return (T)dic[value];
    }

1

ここでは、EnumMember値を持つenum値のケースは考慮されていないことがわかりました。だからここに行きます:

using System.Runtime.Serialization;

public static TEnum ToEnum<TEnum>(this string value, TEnum defaultValue) where TEnum : struct
{
    if (string.IsNullOrEmpty(value))
    {
        return defaultValue;
    }

    TEnum result;
    var enumType = typeof(TEnum);
    foreach (var enumName in Enum.GetNames(enumType))
    {
        var fieldInfo = enumType.GetField(enumName);
        var enumMemberAttribute = ((EnumMemberAttribute[]) fieldInfo.GetCustomAttributes(typeof(EnumMemberAttribute), true)).FirstOrDefault();
        if (enumMemberAttribute?.Value == value)
        {
            return Enum.TryParse(enumName, true, out result) ? result : defaultValue;
        }
    }

    return Enum.TryParse(value, true, out result) ? result : defaultValue;
}

そしてその列挙型の例:

public enum OracleInstanceStatus
{
    Unknown = -1,
    Started = 1,
    Mounted = 2,
    Open = 3,
    [EnumMember(Value = "OPEN MIGRATE")]
    OpenMigrate = 4
}

1

Enumからオブジェクト値を取得するには、Enum.Parseを使用する必要があります。その後、オブジェクト値を特定の列挙値に変更する必要があります。列挙値へのキャストは、Convert.ChangeTypeを使用して行うことができます。次のコードスニペットをご覧ください

public T ConvertStringValueToEnum<T>(string valueToParse){
    return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}

1

このサンプルを試してください:

 public static T GetEnum<T>(string model)
    {
        var newModel = GetStringForEnum(model);

        if (!Enum.IsDefined(typeof(T), newModel))
        {
            return (T)Enum.Parse(typeof(T), "None", true);
        }

        return (T)Enum.Parse(typeof(T), newModel.Result, true);
    }

    private static Task<string> GetStringForEnum(string model)
    {
        return Task.Run(() =>
        {
            Regex rgx = new Regex("[^a-zA-Z0-9 -]");
            var nonAlphanumericData = rgx.Matches(model);
            if (nonAlphanumericData.Count < 1)
            {
                return model;
            }
            foreach (var item in nonAlphanumericData)
            {
                model = model.Replace((string)item, "");
            }
            return model;
        });
    }

このサンプルでは、​​すべての文字列を送信し、を設定できますEnum。あなたの場合はEnum持っていたデータはあなたが望んでいたことを、あなたのように、そのを返すEnumタイプ。


1
newModel各行で上書きしているので、ダッシュが含まれている場合、置換されません。また、あなたは文字列が何も含まれているかどうかをチェックする必要はありません、あなただけ呼び出すことができますReplaceとにかく:var newModel = model.Replace("-", "").Replace(" ", "");
ラース・クリステンセン

@LarsKristensenええ、英数字以外の文字を削除するメソッドを作成できます。
AmirReza-Farahlagha 2018年

1

これがいつ追加されたかはわかりませんが、Enumクラスには

Parse<TEnum>(stringValue)

問題の例でそのように使用されます:

var MyStatus = Enum.Parse<StatusEnum >("Active")

または大文字小文字を区別しない:

var MyStatus = Enum.Parse<StatusEnum >("active", true)

これが使用する逆コンパイルされたメソッドは次のとおりです。

    [NullableContext(0)]
    public static TEnum Parse<TEnum>([Nullable(1)] string value) where TEnum : struct
    {
      return Enum.Parse<TEnum>(value, false);
    }

    [NullableContext(0)]
    public static TEnum Parse<TEnum>([Nullable(1)] string value, bool ignoreCase) where TEnum : struct
    {
      TEnum result;
      Enum.TryParse<TEnum>(value, ignoreCase, true, out result);
      return result;
    }

0
        <Extension()>
    Public Function ToEnum(Of TEnum)(ByVal value As String, ByVal defaultValue As TEnum) As TEnum
        If String.IsNullOrEmpty(value) Then
            Return defaultValue
        End If

        Return [Enum].Parse(GetType(TEnum), value, True)
    End Function

0
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){
if (string.IsNullOrEmpty(value))
    return defaultValue;

return Enum.Parse(typeof(TEnum), value, true);}

0

プロパティ名があなたがそれを呼びたいものと異なる場合(すなわち言語の違い)、あなたはこのようにすることができます:

MyType.cs

using System;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

[JsonConverter(typeof(StringEnumConverter))]
public enum MyType
{
    [EnumMember(Value = "person")]
    Person,
    [EnumMember(Value = "annan_deltagare")]
    OtherPerson,
    [EnumMember(Value = "regel")]
    Rule,
}

EnumExtensions.cs

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

public static class EnumExtensions
{
    public static TEnum ToEnum<TEnum>(this string value) where TEnum : Enum
    {
        var jsonString = $"'{value.ToLower()}'";
        return JsonConvert.DeserializeObject<TEnum>(jsonString, new StringEnumConverter());
    }

    public static bool EqualsTo<TEnum>(this string strA, TEnum enumB) where TEnum : Enum
    {
        TEnum enumA;
        try
        {
            enumA = strA.ToEnum<TEnum>();
        }
        catch
        {
            return false;
        }
        return enumA.Equals(enumB);
    }
}

Program.cs

public class Program
{
    static public void Main(String[] args) 
    { 
        var myString = "annan_deltagare";
        var myType = myString.ToEnum<MyType>();
        var isEqual = myString.EqualsTo(MyType.OtherPerson);
        //Output: true
    }     
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.