定数をグループ化できる列挙体 (Enum) は大変便利な機構です。ただし、Char 以外の整数型しか扱うことができません。
データベース周りのコードを書いていると、フィールドなどをベタに指定したくない場合があります。このような場合、「文字列 (System.String 型) の列挙体があればなぁ」などと思うことでしょう。実際私も VB6 を触っていた頃、喉から手が出るほど欲しい機構でした。
.NET では、このような場合には「型付データセット (DataSet)」を使います。狭義のデータベースである必要もなく、フィールドが表現できるのであれば使えます。
それでも、列挙体の System.String 型バージョンが使いたくてやまない場合。クラスに公開された定数メンバ フィールドを並べたりするハズです。(C# だと読めない (読まない) 方が多いらしいので、VB で書いてみました)
VB.NET
'/** 全日本グループ クラス */
Public MustInherit Class 全日本グループ
Public Const みさき As String = "みさき"
Public Const つばさ As String = "つばさ"
Public Const にゅうが As String = "ひゅうが"
Public Const まさお As String = "まさお"
Public Const にった As String = "にった"
Public Const かずお As String = "かずお"
Public Const まつやま As String = "まつやま"
Public Const じとう As String = "じとう"
Public Const そうだ As String = "そうだ"
Public Const いしざき As String = "いしざき"
Public Const わかばやし As String = "わかばやし"
End Class
'/** ドイツグループ クラス */
Public MustInherit Class ドイツグループ
Public Const シュナイダー As String = "シュナイダー"
Public Const メッツァ As String = "メッツァ"
Public Const マーガス As String = "マーガス"
Public Const シェスター As String = "シェスター"
Public Const カペロマン As String = "カペロマン"
Public Const ポブルセン As String = "ポブルセン"
Public Const カルツ As String = "カルツ"
Public Const ヘルマー As String = "ヘルマー"
Public Const フライハイト As String = "フライハイト"
Public Const ブリクサ As String = "ブリクサ"
Public Const ミューラー As String = "ミューラー"
End Class
この方法の欠点は、「自分で定義した変数名に、その "変数名と同じ文字列" を自分で格納する必要がある」これに尽きると思います。
これでは、面倒な上に typo (打ち損じ) がないとも限りません。これを、常に正しく自動で格納するにはどうすれば良いのか考えてみました。リフレクションを使ってかなり異端な実装をします。
VB.NET
'/** ここでインスタンス化する */
<Microsoft.VisualBasic.CompilerServices.StandardModule()> _
Public NotInheritable Class 標準モジュール扱いのクラス
Public Shared ReadOnly 全日本グループ As New JapanGroup()
Public Shared ReadOnly ドイツグループ As New GermanyGroup()
End Class
'/** BaseFields クラス */
Public MustInherit Class BaseFields
Public Sub New()
Dim hType As System.Type = Me.GetType()
Dim hFieldInfos As System.Reflection.FieldInfo() = hType.GetFields ( _
System.Reflection.BindingFlags.GetField Or _
System.Reflection.BindingFlags.Public Or _
System.Reflection.BindingFlags.Instance Or _
System.Reflection.bindingflags.DeclaredOnly _
)
For Each aFieldInfo As System.Reflection.FieldInfo In hFieldInfos
aFieldInfo.SetValue(Me, aFieldInfo.Name)
Next
End Sub
End Class
'/** 全日本グループ クラス */
Public NotInheritable Class JapanGroup : Inherits BaseFields
Public ReadOnly _
みさき, _
つばさ, _
にゅうが, _
まさお, _
にった, _
かずお, _
まつやま, _
じとう, _
そうだ, _
いしざき, _
わかばやし _
As String
End Class
'/** ドイツグループ クラス */
Public NotInheritable Class GermanyGroup : Inherits BaseFields
Public ReadOnly _
シュナイダー, _
メッツァ, _
マーガス, _
シェスター, _
カペロマン, _
ポブルセン, _
カルツ, _
ヘルマー, _
フライハイト, _
ブリクサ, _
ミューラー _
As String
End Class
この実装ですと、使う直前に必ず「変数名と同じ文字列」が常に正しく格納されます。その代わりインスタンス メンバになってしまうのが玉に瑕です。
初期化を保証しなくて良い、手動で保証するのであれば、静的フィールドを使う方法もあります。詳しくは下記リンク先をご覧ください。
まあ、「こんな方法を取るな」と言われればそれまでですが、定数のように決まりきった値を使いたい、変数名と同じである保証をしたい。という理由があって、DataSet を使わない場面は一応あります。
リンク先のスレッドだと、テーブルおよびインデックスしかサンプルにあがっていないので、DataSet を使うべきという意見が出ていますが、そういう場面でない時、です。テーブルとか関係なく、グループ化された固定文言とでも言うべき (表現が難しい) 場面です。
私の場合は、レポート コンポーネントの動的なヘッダのパターンがあって、その時は、式フィールドを使ったりしたのですが、正直欲しいと思いましたね。
以上、愚かな試みでした... (*_ _)
こんな劣悪な記事を参考にして、損をしている人が出ているようですので、追記します。
当時、タイプセーフな自作 enum を作るという頭がなかったのか、こんなお遊び記事になっていますが、2 つ有効な方法があります。
ひとつは、.NET 標準の enum を使い、カスタム属性で文字列を拡張する方法。拡張メソッドでそれを取得するメソッドを定義すれば汎用的に使えるでしょう。
もうひとつは Java の enum のように自由かつ強力な拡張が可能な以下のような方法です。たとえば、こんな基底クラスを用意して、
VB.NET
Public MustInherit Class CustomEnumBase(Of TKey, TValue)
Private key As TKey
Private value As TValue
Public Sub New(ByVal key As TKey, ByVal value As TValue)
MyClass.key = key
MyClass.value = value
End Sub
Public Overrides Function ToString() As String
Return MyClass.value.ToString()
End Function
End Class
こんな風にメンバを定義します。
VB.NET
Public NotInheritable Class MyCoke
Inherits CustomEnumBase(Of Integer, String)
Public Shared ReadOnly Member1 As New MyCoke(1, "Pepsi NEX")
Public Shared ReadOnly Member2 As New MyCoke(2, "Pepsi Special")
Public Sub New(ByVal key As Integer, ByVal value As String)
MyBase.New(key, value)
End Sub
End Class
こうすれば、通常の enum と同じようなイメージで使用できます。
メンバ同士の比較が必要な場合は (っていうか普通必要ですけど) compare なメソッドを用意して演算子をオーバーロードすると良いでしょう。めんどくさいので書きません。