secretbase.log

.NET/C#/Pythonなど

例外処理は重いのでEnum.TryParseを用いたほうが良い

定義した Enum のメンバーに例えば入力した文字列が合致した場合に変換するといったケースはよくあります。

そういった場合に使えるメソッドが標準で用意されています。

文字列表現を Enum に変換するメソッド

  • Enum.Parse

    • Parse は戻り値として変換結果を返します。変換出来ない場合は、例外を返すメソッドです。
  • Enum.TryParse

    • TryParse は変換出来ない場合は、戻り値として bool を返します。

使用例

ここでは Enum として元素記号(H, He, Li, Be...と元素番号順)を定義します。

  • 入力
    • 入力はなんでも良いのですが、あらかじめAからZのアルファベットの文字列配列を用意し、AからZまで順番に元素記号に変換できるかを試みます。
    • つまり、Aに該当する元素記号は存在せず、Bに該当する元素記号は存在するといった具合です。
      • 半分くらいが存在しないケースになります。
  • 出力
    • 存在した場合はそのメンバーと存在しない場合は 0 を返す、という仕様とします。

ChemicalElementsクラスは下記となります。

static class ChemicalElements
{
    /// <summary>
    /// 元素記号
    /// </summary>
    public enum SymbolsOfElements
    {
        H = 1, He,
        Li, Be, B, C, N, O, F, Ne,
        Na, Mg, Al, Si, P, S, Cl, Ar,
        K, Ca, Sc, Ti, V, Cr, Mn, Fe, Co, Ni, Cu, Zn, Ga, Ge, As, Se, Br, Kr,
        Rb, Sr, Y, Zr, Nb, Mo, Tc, Ru, Rh, Pd, Ag, Cd, In, Sn, Sb, Te, I, Xe,
        Cs, Ba, La, Ce, Pr, Nd, Pm, Sm, Eu, Gd, Tb, Dy, Ho, Er, Tm, Yb, Lu, Hf, Ta, W, Re, Os, Ir, Pt, Au, Hg, Tl, Pb, Bi, Po, At, Rn,
        Fr, Ra, Ac, Th, Pa, U, Np, Pu, Am, Cm, Bk, Cf, Es, Fm, Md, No, Lr, Rf, Db, Sg, Bh, Hs, Mt, Ds, Rg, Cn, Nh, Fl, Mc, Lv, Ts, Og,
    };

    /// <summary>
    /// Enum.Parse による実装
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    static public SymbolsOfElements GetSymbolParse(string name)
    {
        SymbolsOfElements symbol;
        try
        {
            symbol = (SymbolsOfElements)Enum.Parse(typeof(SymbolsOfElements), name);
        }
        catch
        {
            symbol = 0;
        }
        return symbol;
    }


    /// <summary>
    /// Enum.TryParse による実装
    /// </summary>
    /// <param name="name"></param>
    /// <returns></returns>
    static public SymbolsOfElements GetSymbolTryParse(string name)
    {
        SymbolsOfElements symbol;
        Enum.TryParse(name, out symbol);
        return symbol;
    }
}

上記クラスを利用するクラスは下記のような形です。

class Program
{
    static void Main(string[] args)
    {
        Stopwatch sw = new Stopwatch();
    
        // A から Z までの文字列配列を作成する
        var alphabets = Enumerable.Range(0, 26).Select( n => ((char)('A' + n)).ToString() );

        // TryParse による処理
        Console.WriteLine("処理開始 [TryParse実装] ...");
        sw.Start();
        for (int i = 0; i < 100; i++)
        {
            foreach (var alphabet in alphabets)
            {
                var symbol = ChemicalElements.GetSymbolTryParse(alphabet);
            }
        }
        sw.Stop();
        Console.WriteLine($"処理時間: {sw.Elapsed}\n");


        // Parse による処理
        Console.WriteLine("処理開始 [Parse実装] ...");
        sw.Restart();
        for (int i = 0; i < 100; i++)
        {
            foreach (var alphabet in alphabets)
            {
                var symbol = ChemicalElements.GetSymbolParse(alphabet);
            }
        }
        sw.Stop();
        Console.WriteLine($"処理時間: {sw.Elapsed}\n");

        Console.Read();

    }
}

実行結果

処理開始 [TryParse実装] ...
処理時間: 00:00:00.0036813

処理開始 [Parse実装] ...
処理時間: 00:00:05.8251481

いずれも同じ結果となる処理が、TryParseの場合は、4ms 未満に対し、Parseの場合は、5.8秒となりました。 1500倍くらい差がありますね。

例外処理は重いので Enum.TryParse を用いましょう。