連載
» 2017年04月05日 05時00分 UPDATE

.NET TIPS:列挙体の値を任意の文字列に変換するには?[C#/VB]

列挙体の値を文字列に変換するには、ToStringメソッドを使うのが簡単だが、任意の文字列に変換するには変換テーブルを使用する。また、処理を拡張メソッドに切り分けてもよい。

[山本康彦,BluewaterSoft/Microsoft MVP for Windows Development]
「.NET TIPS」のインデックス

連載目次

 列挙体(または列挙型)の値、すなわち列挙値を文字列に変換したいことがある。列挙子(あるいは列挙メンバー)の名前に変換するなら簡単だが、(列挙子と対応付けられた)任意の文字列に変換するにはちょっと手間が掛かる。本稿では、それらの方法を解説する。

 なお、列挙体の基本となるEnumクラス(System名前空間)は.NET Frameworkの最初からあるものだが、本稿はそれ以降の内容も含んでいる。サンプルコードをそのまま試すには、Visual Studio 2015(またはそれ以降)が必要である。

列挙値を文字列に変換するには?

 列挙子の名前に変換するなら、そのToStringメソッドを使えばよい。列挙子と対応付けた任意の文字列に変換するには、Dictionaryクラス(System.Collections.Generic名前空間)を利用した変換テーブルを用意しておく(次のコード)。

 なお、特別な場合には、Dictionaryクラスの代わりに文字列の配列を使える(後述)。また、変換テーブル/配列の参照をする処理は、拡張メソッドにしておくとコードが分かりやすくなる(後述)。

// 列挙体の定義
enum e { a, b, c };

……省略……

var e1 = e.a;

// 列挙子の名前「a」を得る
string memberName = e1.ToString()// ⇒ a

// 変換テーブルを使って対応する文字列を得る
var table = new Dictionary<e, string>
                { { e.a, "A" }, { e.b, "B" }, { e.c, "C" } };
string relatedName = table[e1]; // ⇒ A

' 列挙体の定義
Enum e
  a
  b
  c
End Enum

……省略……

Dim e1 = e.a

' 列挙子の名前「a」を得る
Dim memberName As String = e1.ToString() ' ⇒ a

' 変換テーブルを使って対応する文字列を得る
Dim table = New Dictionary(Of e, String) _
                From {{e.a, "A"}, {e.b, "B"}, {e.c, "C"}}
Dim relatedName As String = table(e1) ' ⇒ A

列挙値を文字列に変換する方法(上:C#、下:VB)

列挙子の名前を得る例

 列挙値のToStringメソッドで、その列挙値に割り当てられている列挙子の名前が得られる。また、EnumクラスのGetNamesメソッドを使うと、全ての列挙子の名前を得ることもできる。次のコードにコンソールアプリの例を示す。

using System;
using static System.Console;

public enum JapaneseEraName1 { 明治 = 1, 大正, 昭和, 平成, }

class Program
{
  static void Main(string[] args)
  {
    var era = JapaneseEraName1.平成;
    string eraName = era.ToString();
    WriteLine(eraName);
    // 出力:平成

    // 全ての名前を得る
    string[] eraNames = Enum.GetNames(typeof(JapaneseEraName1));
    WriteLine(string.Join(", ", eraNames));
    // 出力:明治, 大正, 昭和, 平成

#if DEBUG
    ReadKey();
#endif
  }
}

Imports System.Console

Public Enum JapaneseEraName1
  明治 = 1
  大正
  昭和
  平成
End Enum

Module Module1
  Sub Main()
    Dim era = JapaneseEraName1.平成
    Dim eraName As String = era.ToString()
    WriteLine(eraName)
    ' 出力:平成

    ' 全ての名前を得る
    Dim eraNames As String() = [Enum].GetNames(GetType(JapaneseEraName1))
    WriteLine(String.Join(", ", eraNames))
    ' 出力:明治, 大正, 昭和, 平成

#If DEBUG Then
    ReadKey()
#End If
  End Sub
End Module

列挙子の名前を得るコンソールアプリの例(上:C#、下:VB)
Enumクラスのメソッドを呼び出すとき、VBでは「Enum」を角かっこで囲む必要がある。
C#コードの冒頭から2行目にある「using static System.Console;」という書き方は、Visual Studio 2015からのものだ。詳しくは、「.NET TIPS:構文:クラス名を書かずに静的メソッドを呼び出すには?[C# 6.0]」をご覧いただきたい。同様な機能がVBには以前から備わっており、「.NET TIPS:VB.NETでクラス名を省略してメソッドや定数を利用するには?」で解説している。
また、「Main」メソッド末尾にReadKeyメソッドを置く意味は、「.NET TIPS:Visual Studioでコンソール・アプリケーションのデバッグ実行時にコマンド・プロンプトを閉じないようにするには?」をご覧いただきたい。
なお、列挙子の名前は、そのアプリで最もよく使われる文字列にしておくのが原則だ。そうしておけば、ほとんどの場面でToStringメソッドを呼び出すだけで済むからだ。ただし、この例のように列挙子の名前として日本語を使うことは、なぜか嫌われている。「日本語を理解できない開発者がメンテナンスできなくなる」という理由が語られることがあるが、次に挙げるサンプルコードのように変換テーブルをハードコーディングしてしまったら同じことだ(その理由が正当なときは、文字列リテラルの全てを文字列リソースとして切り出すべきであろう)。

変換テーブルを使って任意の文字列を得る例

 EnumクラスのToStringメソッドで得られる列挙子の名前とは違う文字列を得たい場合は、変換テーブルを参照する方法が基本になる。例えば、元号の正式名称と略称を得るには次のコードのようにする。なお、コードの中で略称の一覧も取得しているが、汎用的な記述はこのように面倒なものになる。

public enum JapaneseEraName2
{
  Meiji=1868, Taisho=1912, Showa=1926, Heisei=1989,
}

……省略……

// JapaneseEraName2の値を元号の正式名称へ変換するためのテーブル
// (冒頭に「using System.Collections.Generic;」が必要)
static Dictionary<JapaneseEraName2, string> EraFullNames2
  = new Dictionary<JapaneseEraName2, string>
  {
    {JapaneseEraName2.Meiji, "明治"},
    {JapaneseEraName2.Taisho, "大正"},
    {JapaneseEraName2.Showa, "昭和"},
    {JapaneseEraName2.Heisei, "平成"},
  };
// JapaneseEraName2の値を元号の略称へ変換するためのテーブル
static Dictionary<JapaneseEraName2, string> EraShortNames2
  = new Dictionary<JapaneseEraName2, string>
  {
    {JapaneseEraName2.Meiji, "M"},
    {JapaneseEraName2.Taisho, "T"},
    {JapaneseEraName2.Showa, "S"},
    {JapaneseEraName2.Heisei, "H"},
  };

……省略……

var era = JapaneseEraName2.Heisei;
string eraFullName = EraFullNames2[era];
string eraShortName = EraShortNames2[era];
WriteLine($"{eraFullName}({eraShortName})");
// 出力:平成(H)

// 全ての略称を得る(冒頭に「using System.Linq;」が必要)
IEnumerable<string> shortNames
  = Enum.GetValues(typeof(JapaneseEraName2))
        .Cast<JapaneseEraName2>().Select(e => EraShortNames2[e]);
WriteLine(string.Join(", ", shortNames));
// 出力:M, T, S, H

Public Enum JapaneseEraName2
  Meiji = 1868
  Taisho = 1912
  Showa = 1926
  Heisei = 1989
End Enum

……省略……

' JapaneseEraName2の値を元号の正式名称へ変換するためのテーブル
Private EraFullNames2 As New Dictionary(Of JapaneseEraName2, String) _
  From
  {
    {JapaneseEraName2.Meiji, "明治"},
    {JapaneseEraName2.Taisho, "大正"},
    {JapaneseEraName2.Showa, "昭和"},
    {JapaneseEraName2.Heisei, "平成"}
  }

' JapaneseEraName2の値を元号の略称へ変換するためのテーブル
Private EraShortNames2 As New Dictionary(Of JapaneseEraName2, String) _
  From
  {
    {JapaneseEraName2.Meiji, "M"},
    {JapaneseEraName2.Taisho, "T"},
    {JapaneseEraName2.Showa, "S"},
    {JapaneseEraName2.Heisei, "H"}
  }

……省略……

Dim era = JapaneseEraName2.Heisei
Dim eraFullName As String = EraFullNames2(era)
Dim eraShortName As String = EraShortNames2(era)
WriteLine($"{eraFullName}({eraShortName})")
' 出力:平成(H)

' 全ての略称を得る
Dim shortNames As IEnumerable(Of String) _
  = [Enum].GetValues(GetType(JapaneseEraName2)) _
          .Cast(Of JapaneseEraName2)() _
          .Select(Function(e) EraShortNames2(e))
WriteLine(String.Join(", ", shortNames))
' 出力:M, T, S, H

変換テーブルを使って任意の文字列を得る例(上:C#、下:VB)
列挙子に割り当てられる整数値は、この例のように不連続なことも多い。そのため、汎用的に変換するコードを書くには、このように変換テーブルを使う必要がある。
また、全ての略称を得るコードが複雑なものになっているのも、汎用的なコードとして書いたためである。もしもEraShortNames2の定義順序がJapaneseEraName2と同じであり、かつ、EraShortNames2が余分な要素を持っていないならば、「EraShortNames2.Values」でよい。
なお、Dictionaryクラスの初期化方法については、「.NET TIPS:Dictionaryクラスを簡単に初期化するには?[C# 3.0]」をご覧いただきたい。
最初のWriteLineメソッドに出てくる先頭に「$」記号が付いた文字列については、「.NET TIPS:数値を右詰めや0埋めで文字列化するには?[C#、VB]」の後半(「C# 6.0/VB 14で追加された補間文字列機能を使用する」)を見てほしい。

変換テーブルを参照する処理を拡張メソッドにして分かりやすくする例

 変換テーブルを参照する処理を拡張メソッドにしておくと、コードが分かりやすくなる(次のコード)。

// 列挙体JapaneseEraName2の定義は前のサンプルコードと同じなので省略

public static class JapaneseEraName2Extension
{
  // JapaneseEraName2の値を元号の正式名称へ変換するためのテーブル
  static Dictionary<JapaneseEraName2, string> EraFullNames2 
    = new Dictionary<JapaneseEraName2, string>
    {
      {JapaneseEraName2.Meiji, "明治"},
      {JapaneseEraName2.Taisho, "大正"},
      {JapaneseEraName2.Showa, "昭和"},
      {JapaneseEraName2.Heisei, "平成"},
    };
  // JapaneseEraName2の値を元号の正式名称へ変換する拡張メソッド
  public static string ToFullName(this JapaneseEraName2 era)
    => EraFullNames2[era];

  // JapaneseEraName2の値を元号の略称へ変換するためのテーブル
  static Dictionary<JapaneseEraName2, string> EraShortNames2 
    = new Dictionary<JapaneseEraName2, string>
    {
      {JapaneseEraName2.Meiji, "M"},
      {JapaneseEraName2.Taisho, "T"},
      {JapaneseEraName2.Showa, "S"},
      {JapaneseEraName2.Heisei, "H"},
    };
  // JapaneseEraName2の値を元号の略称へ変換する拡張メソッド
  public static string ToShortName(this JapaneseEraName2 era)
    => EraShortNames2[era];
}

……省略……

var era = JapaneseEraName2.Heisei;
string eraFullName = era.ToFullName();
string eraShortName = era.ToShortName();
WriteLine($"{eraFullName}({eraShortName})");
// 出力:平成(H)

' 列挙体JapaneseEraName2の定義は前のサンプルコードと同じなので省略

Public Module JapaneseEraName2Extension
  ' JapaneseEraName2の値を元号の正式名称へ変換するためのテーブル
  Private EraFullNames2 As New Dictionary(Of JapaneseEraName2, String) _
    From
    {
      {JapaneseEraName2.Meiji, "明治"},
      {JapaneseEraName2.Taisho, "大正"},
      {JapaneseEraName2.Showa, "昭和"},
      {JapaneseEraName2.Heisei, "平成"}
    }

  ' JapaneseEraName2の値を元号の正式名称へ変換する拡張メソッド
  <System.Runtime.CompilerServices.Extension()>
  Public Function ToFullName(era As JapaneseEraName2) As String
    Return EraFullNames2(era)
  End Function

  ' JapaneseEraName2の値を元号の略称へ変換するためのテーブル
  Private EraShortNames2 As New Dictionary(Of JapaneseEraName2, String) _
    From
    {
      {JapaneseEraName2.Meiji, "M"},
      {JapaneseEraName2.Taisho, "T"},
      {JapaneseEraName2.Showa, "S"},
      {JapaneseEraName2.Heisei, "H"}
    }

  ' JapaneseEraName2の値を元号の略称へ変換する拡張メソッド
  <System.Runtime.CompilerServices.Extension()>
  Public Function ToShortName(era As JapaneseEraName2) As String
    Return EraShortNames2(era)
  End Function

End Module

……省略……

Dim era = JapaneseEraName2.Heisei
Dim eraFullName As String = era.ToFullName()
Dim eraShortName As String = era.ToShortName()
WriteLine($"{eraFullName}({eraShortName})")
' 出力:平成(H)

変換テーブルを参照する処理を拡張メソッドにして分かりやすく(上:C#、下:VB)
C#のToFullNameメソッド/ToShortNameメソッドの書き方については、「.NET TIPS:構文:メソッドやプロパティをラムダ式で簡潔に実装するには?[C# 6.0]」をご覧いただきたい。

変換テーブルの代わりに配列を使う例

 列挙子に割り当てた整数が0始まりで連続しているときは、変換テーブルに代えて次のコードのように文字列の配列が使える(多少の不連続があっても可能)。

public enum JapaneseEraName3 { Meiji, Taisho, Showa, Heisei, }

……省略……

// JapaneseEraName3の値をインデックスとする元号の正式名称の配列
static string[] EraFullNames3 = { "明治", "大正", "昭和", "平成", };
// JapaneseEraName3の値をインデックスとする元号の略称の配列
static string[] EraShortNames3 = { "M", "T", "S", "H", };

……省略……

var era = JapaneseEraName3.Heisei;
string eraFullName = EraFullNames3[(int)era];
string eraShortName = EraShortNames3[(int)era];
WriteLine($"{eraFullName}({eraShortName})");
// 出力:平成(H)

Public Enum JapaneseEraName3
  Meiji
  Taisho
  Showa
  Heisei
End Enum

……省略……

' JapaneseEraName3の値をインデックスとする元号の正式名称の配列
Private EraFullNames3 As String() = {"明治", "大正", "昭和", "平成"}
' JapaneseEraName3の値をインデックスとする元号の略称の配列
Private EraShortNames3 As String() = {"M", "T", "S", "H"}

……省略……

Dim era = JapaneseEraName3.Heisei
Dim eraFullName As String = EraFullNames3(era)
Dim eraShortName As String = EraShortNames3(era)
WriteLine($"{eraFullName}({eraShortName})")
' 出力:平成(H)

変換テーブルの代わりに配列を使う例(上:C#、下:VB)
列挙体を宣言するとき、この例のように整数を暗黙的に割り当てた場合は、0から連続して順番に割り当てられる。そのため、配列のインデックスとして利用できる。
なお、配列を使う場合も、変換テーブルのときと同じように拡張メソッドにしておくと分かりやすくなる。また、拡張メソッドにカプセル化しておけば、もしも仕様変更によって配列が使えなくなったときでも対応が簡単になる。

まとめ

 列挙値を任意の文字列に変換するには、変換テーブルか配列を参照する。その変換ロジックは拡張メソッドにしておくとコードが分かりやすくなる。特に配列を使うときには、将来の仕様変更に備えて、変換ロジックを拡張メソッドにカプセル化しておくとよい。

「.NET TIPS」のインデックス

.NET TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

Focus

- PR -

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。