じゃんぬねっと日誌

ネタと雑記と時々プログラミング

目次

Blog 利用状況

ニュース

不況すぎる件。

スポンサードリンク

運営サイト

  • C# と VB.NET の入門サイト

書庫

文字列の列挙体

定数をグループ化できる列挙体 (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 を使うべきという意見が出ていますが、そういう場面でない時、です。テーブルとか関係なく、グループ化された固定文言とでも言うべき (表現が難しい) 場面です。

私の場合は、レポート コンポーネントの動的なヘッダのパターンがあって、その時は、式フィールドを使ったりしたのですが、正直欲しいと思いましたね。

以上、愚かな試みでした... (*_ _)

投稿日時 : 2006年4月4日 11:33

コメントを追加

# re: 文字列の列挙体 2006/04/04 12:02 じゃんぬ

簡単に言えば、
 使うためにはインスタンス化しなくてはならない
->インスタンス化すると基底クラスのデフォルト コンストラクタが実行される
->コンストラクタでリフレクションを使って "変数名" を取得してそのまま格納
とされるので中身も保証されると。

ちなみに、
 Dim hType As System.Type = Me.GetType()
を、
 Dim hType As System.Type = MyClass.GetType()
にすると、Me と MyClass の違いもわかることでしょう。

いや... あのね、ホント空しいので何かそれっぽいこと書いておきたいんですよ。

# re: 文字列の列挙体 2006/04/04 15:29 囚人

>(C# だと読めない (読まない) 方が多いらしいので、VB で書いてみました)

VB だと読みにくい(読まない…ことはないが)。

# re: 文字列の列挙体 2006/04/04 16:03 中博俊

型付データセットを使えばそれでいいこと
というか型付けないデータセットは使えない

# re: 文字列の列挙体 2006/04/04 16:24 じゃんぬ

> VB だと読みにくい(読まない…ことはないが)

えっと、C# で書く場合と考えると問題のない範囲ですw

> 型付データセットを使えばそれでいいこと

これは正味の DataSet の使い方をしないんですけどね。

中身は格納して決めるんじゃなくて、定数のように決まっていて欲しい。
でも定数とは違って、定数名と中身は同一であって欲しいとか。
確かにあると便利な場面はあります。
たとえば、CrystalReports のヘッダ代わりの式フィールドとかね。

# re: 文字列の列挙体 2006/04/06 10:12 某はいくーぬ

System.Enum.GetNames(GetType(列挙体名))

CType(System.Enum.Parse(GetType(列挙体名), 定数名), 列挙体名)
とかはダメ?

# re: 文字列の列挙体 2006/04/06 10:48 じゃんぬ

> とかはダメ?

これだと、DataSet みたいに ほげ.フィールド1 というスタイルで使用できないですね。

まず、Type を渡すのがあまりに面倒です。
ラップするメソッドを書いたとしても定数名は、
String で固定できるものじゃないですね。
列挙体のようにコンパイル解決したいのが狙いですから。

# re: 文字列の列挙体 2006/04/16 12:43 名無し

ブラジルはこうだww

11 ネイ
10 コインブラ
09 カルロス・サンターナ
08 トニーニョ
07 ザガロ
06 サンタマリア
05 ディウセウ
04 アマラウ
03 ドトール
02 ジェトーリオ
01 ゲルティス

# re: 文字列の列挙体 2006/10/27 4:33 BLUEPIXY

ほげ.フィールド1.ToString();//C#
でダメ?

# re: 文字列の列挙体 2006/10/27 8:58 じゃんぬ

>BLUEPIXY さん
経緯はリンク先の方をご覧ください。
ちなみに、Enum->ToString メソッドは推奨されていないです。
(警告が出ます)

# re: 文字列の列挙体 2006/10/27 17:23 BLUEPIXY

リンク先ざっと見ましたけど、どちらかというと、その後の話みたいでもう一つよく見えませんでした。
要らぬちゃちゃを入れて不快に思われたのならすみません。

# re: 文字列の列挙体 2006/10/27 17:26 BLUEPIXY

ちなみに、
csc for .NET 1.1
csc for .NET 2.0
のどちらも警告はでませんでしたが・

# re: 文字列の列挙体 2006/10/27 17:35 BLUEPIXY

また、MSDNのサンプル
Enum, Enum.ToString
で、実際に使っているサンプルがあり、推奨されないとは書いていませんが・

なんか、MSDNのリンクが書けないですね。
コメントの文字数制限かと思って分断して、削って素っ気なくなってしまいました。

# re: 文字列の列挙体 2006/10/27 19:01 じゃんぬ

BLUEPIXY さん、コメントありがとうございます。

> 要らぬちゃちゃを入れて不快に思われたのならすみません。

いえ、不快には全然思っていないです。
文章だとどうも素っ気無く見えてしまうようです。

> どちらも警告はでませんでしたが

Visual Studio 2005 だと、インテリセンス上に、
「使用しないでください」
と表示されたかと思います。

> また、MSDNのサンプル
> Enum, Enum.ToString
> で、実際に使っているサンプルがあり、推奨されないとは書いていませんが・

ここは、ちょっと誤解があるように思えます。
MSDN ライブラリの使用例などのコードは、自動で生成されたものです。
辻褄のあっていないものはたくさんあります。

私も含め、Microsoft MVP な人が、結構フィードバックしていますよ。

# re: 文字列の列挙体 2006/10/28 2:55 BLUEPIXY

>Visual Studio 2005 だと、インテリセンス上に、「使用しないでください」 と表示されたかと思います。
そうですか、「Visual Studio 2005」環境構築していないので気付きませんでした。しかしまあ、変な話ではありますね。
結局の処、インテリセンスが、推奨されないという根拠なワケですね。
1つ賢くなりました。
ご教授ありがとうございました。
m(_ _)m

# re: 文字列の列挙体 2006/10/28 3:23 BLUEPIXY

>MSDN ライブラリの使用例などのコードは、自動で生成されたものです。
>辻褄のあっていないものはたくさんあります。
間違ったコードが書かれていることがあるというのは、わかります。
だけれども、自動で生成されたからと言って、それを理由にすぐさまそんなのは根拠にならないということにはならないと思います。
自動生成ということは、おそらくテストUnit?みたいなことなんでしょうけど、
例えば、Enum クラスのサンプルを見ると、
WriteLine を使ってフィールド名を(暗黙のToStringで)表示していますが、それは、もちろん、自動生成されたからではなくて、そのような意図(フィールド名が文字列要求されたときにはその名前を返すということのテスト)をもってテスト生成されたということですよね。

# re: 文字列の列挙体 2006/10/28 9:20 じゃんぬ

推奨されていない理由は、FlagAttribute 属性関係を危惧してのことだと思います。
通常は (今は) 問題ないのでしょうけど、CLR の実装が変わればできなくなる可能性もあります。
今現在、すでにインテリセンス上では「使用するな」とまで言われているわけですから。

それと、単一で定義されていないものを ToString メソッドで呼び出せてしまう (普通はしませんが) のも、なるべく避けたいですし、何より、列挙体本来の使い方ではないので、わかりにくいですね。
少なくとも、リフレクションを使った方法よりか推奨されないと考えています。

リフレクションを使った方法では、保証があるのとフィールド名までのパスが明確でわかりやすいのが利点なので、(あとは面白そうだったので) こちらの記事では、これのみを紹介しました。

> そのような意図 をもってテスト生成されたということですよね

そのような意図の結果、間違ったコードを書いていることもありますし、その意図 "自体" が間違っていることさえあります。
今回の件も、インテリセンスとの相違で (というか記述がないだけですが) その片鱗を見せているわけで、結局のところ Microsoft 内でも辻褄が合っていないことは多々あります。
COM Interop 関係とファイナライザの記述も、過去に辻褄があっていませんでした。

# re: 文字列の列挙体 2006/11/01 4:13 BLUEPIXY

ToString("G")
は、どうでしょう?
この場合、列挙型エントリを文字列として(表示できる場合には)作成するので、意味としてははっきりしていると思います。
MSDNの"列挙型書式指定文字列"を読んでみて下さい。
この場合、これが、辻褄が合わないなんとかかんとかというなら、このエントリ自体おかしいと思います。

# re: 文字列の列挙体 2006/11/14 17:11 BLUEPIXY

>Microsoft MVP な人が
ちなみに
Microsoft MVP な人「中 博俊」さんも
その著作「実践C++/CLI 極めるための基礎と実用テクニック」という一般向けの本の中でToStringメソッドで列挙体の列挙子を取得できると書いてます。
(ちなみにということで、コレを使えという意味ではありませんし、だからどうということもありません)

# re: 文字列の列挙体 2006/11/23 19:03 BLUEPIXY

>今現在、すでにインテリセンス上では「使用するな」とまで言われているわけですから。
例えば、VC++で、strcpyとかが、strcpy_s を使用するようにとコンパイラで警告がでることを考えると、
コンパイラで警告がでないインテリセンスのメッセージは(辻褄が合わないと言うより)(推奨や警告ではなく)単なるアドバイスに過ぎないと思います。

# re: 文字列の列挙体 2006/11/23 22:00 中博俊

C#のコンパイラはライブラリに基本的に踏み込みませんから、それはちがうでしょう。

# re: 文字列の列挙体 2006/11/25 2:52 BLUEPIXY

そうなんですか、訂正ありがとうございます。

# re: 列挙型を使う(その5) 2007/03/17 11:46 R.Tanaka.Ichiro's Blog

re: 列挙型を使う(その5)

# re: 文字列の列挙体 2007/03/18 9:57 名無し

> Microsoft MVP な人「中 博俊」さんも

とか言ってるけど、Microsoft MVPな人「じゃんぬねっと」さんに対して言う言葉じゃないね。

それと、こちらのサイト(わんくま同盟さん)は、その中博俊さんとじゃんぬねっとさんによって運営されていることをご存知ないのかしら?

よりによって、対抗馬で中博俊さんを出してくること自体がなんだかな。
会話もかみ合ってないし。

BLUEPIXIさんって、OKWebでも何かとずれてると思っていたけど、こんなところでもずれてるんですね。

# 列挙型を使う(その6) 2007/03/19 10:03 R.Tanaka.Ichiro's Blog

列挙型を使う(その6)

# 列挙型を使う(その6) 2007/03/19 10:07 R.Tanaka.Ichiro's Blog

列挙型を使う(その6)

# re: 文字列の列挙体 2007/03/22 20:35 BLUEPIXY

ずれたことを書くのは本意ではないので、一応一連の私の書込について説明しておくと、私の一連の書込は、記事が云々ということではなくて、「ToStringではだめな根拠は何なの?」ということです。
記事と関連性がないずれたコメントだと思われたならすみませんです。
MVPがどうのこうのというのは、知らなくて言ってるのではなくて、MVPが根拠になるの?ということです。
一連の書込は
「推奨されない」根拠がインテリセンスのメッセージによるなら、そういう開発環境を使っていない人はどうしてそれを知ることができるのかという疑問が根底にあります。
やっぱずれてますかねぇf(^_^;

# re: 文字列の列挙体 2007/05/16 21:10 名無し

>MVPがどうのこうのというのは、知らなくて言ってるのではなくて、MVPが根拠になるの?ということです。

じゃあこの書き込みは何?

>Microsoft MVP な人「中 博俊」さんも
>その著作「実践C++/CLI 極めるための基礎と実用テクニック」という一般向けの本の中でToStringメソッドで列挙体の列挙子を取得できると書いてます。

MVPがどうこう関係ないの反論の意味だったらそう書けばいいのに。
問題なのはフィードバックしてるという事実じゃないの?
別にMVPをかばう意図はないけどさ。

# Stringの名前とデフォルト値を持つ定数 2008/07/05 23:39 katamari.wankuma.com

Stringの名前とデフォルト値を持つ定数

# Stringの名前とデフォルト値を持つ定数 2008/07/05 23:43 katamari.wankuma.com

Stringの名前とデフォルト値を持つ定数

タイトル  
名前  
URL
コメント