【C#】文字列や数値をenum型に変換する

ある任意の文字列や数値から特定のEnumに変換する方法です。

確認環境

  • C#8.0/7.3
  • VisualStudio2019
  • .NET Framwework 4.8/4.7.3
  • Windows10

バージョン依存性があるコードが含まれます。

文字列をEnumに変換する方法

まず enum が以下のように定義されているとします。

// 以下のようにenumが定義されているものとする
public enum EnumSample
{
    Ika = 0,
    Tako,
    Suzuki
}

シンボル名文字列から enum 型への変換

あるEnumのシンボル名からEnum形への変換方法jは以下の通りです。

変換は標準の機能として Enum クラスに Parse 系のメソッドが存在するためこれを利用します。

public static void Foo()
{
    // 変換方法(1) .NET Core の場合
    //
    // 補足:
    // この方法は.NET Core 限定。NET Framework にはメソッドが存在しない
    //
    // 変換できることが確実な場合このように記述する
    // 変換できない場合、「System.ArgumentException: 'Requested value 'sake' was not found.'」
    // 
    var e1 = Enum.Parse<EnumSample>("sake");

    // 変換方法(2) .NET Framework の場合
    //
    // 変換できることが確実な場合このように記述する
    // 変換できない場合、「System.ArgumentException: 'Requested value 'sazae' was not found.'」
    //
    var e2 = (EnumSample)Enum.Parse(typeof(EnumSample), "sazae");

    // 変換方法(2), .NET Core/Framework 共通
    //
    // 変換できるか分からないときにこちらを使用して
    // 変換できたか確認してから使用する。
    //
    if (Enum.TryParse("ika", out EnumSample result))
    {
        // 変換が成功したときに result に結果が入っている
    }
    else
    {
        // 失敗した場合こっち
    }
}

ターゲットプラットフォームが変更される場合、ジェネリック指定できたほうが記述が簡単になるので、上記変換(1), (2)はまとめて以下のように定義していた方が便利かもしれません。

// .NET Framework にジェネリック版の Parse メソッドが存在しないが .NET Core と同じように扱う対応
public static class MyEnum
{
#if NETFRAMEWORK
    public static TEnum Parse<TEnum>(string name) where TEnum : struct
    {
        return (TEnum)Enum.Parse(typeof(TEnum), name);
    }
#else
    public static TEnum Parse<TEnum>(string name) where TEnum : struct
    {
        return Enum.Parse<TEnum>(name);
    }
#endif
}

数字文字列から enum 型への変換

ある文字列変数に "1" などの数値文字列が入っている場合、enum 型に存在しない値でも変換できてしまうため Enum.IsDefined() メソッドで数字に対応する enum のシンボルが定義されているか確認する必要があります。

基本的に先述のシンボル名文字列からの変換と同じですが変換する前に IsDefined を使って存在するかどうかを確認してから変換を行います。

// 変換方法(1) .NET Core の場合
//
// 補足:
// この方法は.NET Core 限定。NET Framework にはメソッドが存在しない
//
// 変換できることが確実な場合このように記述する
// 変換できない場合、「System.ArgumentException: 'Requested value 'sake' was not found.'」
// 
string str1 = "10";
if (Enum.IsDefined(typeof(EnumSample), str1)) // 事前に定義が存在するかどうか確認する
{
    var e1 = Enum.Parse<EnumSample>(str1);
}

// 変換方法(2) .NET Framework の場合
//
// 変換できることが確実な場合このように記述する
// 変換できない場合、「System.ArgumentException: 'Requested value 'sazae' was not found.'」
//
string str2 = "Tako";
if (Enum.IsDefined(typeof(EnumSample), str2))
{
    var e2 = (EnumSample)Enum.Parse(typeof(EnumSample), str2);
}

// 変換方法(2), .NET Core/Framework 共通
//
// 変換できるか分からないときにこちらを使用して
// 変換できたか確認してから使用する。
//
string str3 = "Ika";
if (Enum.IsDefined(typeof(EnumSample), str3) && 
    Enum.TryParse(str3, out EnumSample result))
{
    // 変換が成功したときに result に結果が入っている
}
else
{
    // 失敗した場合こっち
}

数値から enum 型への変換

次は数値から enum 型への変換です。

変換自体は数値を enum 型にキャストするだけで変換可能ですが、こちらも存在しないシンボルの値にも変換できてしまうため事前に IsDefined で確認が必要になります。

// 変換方法(1)、普通にキャストする
int num1 = 0;
if (Enum.IsDefined(typeof(EnumSample), num1)) // 事前に定義が存在するかどうか確認する
{
    var e1 = (EnumSample)num1; // キャストで変換できる
}

// 変換方法(2) TryPaeseを使う
int num2 = 10;
if (Enum.IsDefined(typeof(EnumSample), num2) && 
    Enum.TryParse(num2.ToString(), out EnumSample result))
{
    // 変換が成功したときに result に結果が入っている
}
else
{
    // 失敗した場合こっち
}

相互変換をサポートするクラス

3種類のケースを見てきましたが結局文字列に何が入っているかで書き分ける必要があり、数値の場合とコードが違うなどで面倒なため、全て1つのメソッド解決できるユーティリティを紹介したいと思います。

使い方

先に使い方を紹介します。EnumUtil クラスに Parse, TryParse を実装しています。

//
// (1) Parse メソッドの使い方
//

var e0 = EnumUtil.Parse<EnumSample>("10");
// > 値が存在しないので ArgumentException

var e1 = EnumUtil.Parse<EnumSample>("Tako");
// > e1 = Tako

var e2 = EnumUtil.Parse<EnumSample>(2);
// > e1 = Suzuki

var e3 = EnumUtil.Parse<EnumSample>("2");
// > これはエラーになるので注意!

//
// (2) TryParse メソッドの使い方
//

if (EnumUtil.TryParse("10", out EnumSample result1))
{
    // 変換できたとき
}
else
{
    // 変換できなかったとき
}

// 大文字・小文字を区別したくない時は igoreCase = true で使用する
if (EnumUtil.TryParse("tako", true, out EnumSample result2))
{
    // 変換できたとき
}
else
{
    // 変換できなかったとき
}

EnumUtilクラス

実際の実装です。

.NET Framework でも使用可能なようにジェネリック版の Parse は使用していません。

// 任意の値を enum 型に変換するための機能を定義します
public static class EnumUtil
{
    public static TEnum Parse<TEnum>(object value, bool ignoreCase = false) where TEnum : struct
    {
        if (Enum.IsDefined(typeof(TEnum), value))
        {
            return (TEnum)Enum.Parse(typeof(TEnum), value.ToString(), ignoreCase);
        }
        else
        {
            throw new ArgumentException($"'{value}' is not found.");
        }
    }

    public static bool TryParse<TEnum>(object value, out TEnum result) where TEnum : struct
    {
        result = default;
        return Enum.IsDefined(typeof(TEnum), value) && 
            Enum.TryParse(value.ToString(), out result);
    }

    public static bool TryParse<TEnum>(object value, 
        bool ignoreCase, out TEnum result) where TEnum : struct
    {
        result = default;
        return Enum.IsDefined(typeof(TEnum), value) && 
            Enum.TryParse(value.ToString(), ignoreCase, out result);
    }
}

文字列変数を直接 enum に変換する

ここからは余談で、string 型に拡張メソッドを定義して直接 enum に変換してみます。

使い方

使い方は以下の通りです。

string name1 = "Tako";

// (1) 文字列の変数から直接 Parse して enum を得る
var e1 = name.Parse<EnumSample>();

string name2 = "Same";

// (2) 文字列の変数から直接 TryParse して変換できるか確認後に enum を得る
if(name.TryParse(out EnumSample ret))
{
    // 変換に成功した場合
}
else
{
    // 失敗した場合
}

実装コード:StringExtensionクラス

実装はすごく簡単で先ほどの EnumUtil を間接的に使用します。

public static class StringExtension
{
    // 文字列から TEnum で指定した enum に変換する
    public static TEnum Parse<TEnum>(this string self) where TEnum : struct
    {
        return EnumUtil.Parse<TEnum>(self);
    }

    public static bool TryParse<TEnum>(this string self, out TEnum result) where TEnum : struct
    {
        return EnumUtil.TryParse(self, out result);
    }

    public static bool TryParse<TEnum>(this string self, 
        object value, bool ignoreCase, out TEnum result) where TEnum : struct
    {
        return EnumUtil.TryParse(self, ignoreCase, out result);
    }
}

少し長くなってしまいましたが以上です。

関連リンク

takap-tech.com