C# Tips

C#のTipsです。ご自由にどうぞ。
ただし、コードを利用したことによって何かあっても一切保障しません。自己責任でお願いします。
下からもアクセスできますが、一部のTipsは別のページです。
ダイアログ関連
Tips名 説明 作成日、更新日
デザイナかどうか判断する 現在VisualStudioのフォームデザイナでコードが実行しているかを判断します 2011/3/22
アイコンダイアログを表示 アイコンを指定する隠しダイアログを開いてアイコンの場所とインデックスを取得します。 2011/3/22、2011/3/24
Windows終了ダイアログを表示 シャットダウンボタンを押したときに表示されるダイアログを出します。 2011/3/24
ファイルのプロパティを表示する ファイルやフォルダのプロパティを開きます。 2011/3/25
再起動を促すダイアログを表示 インストール後などにでる再起動やシャットダウンを促すダイアログを表示します。 2011/3/26
ファイル名を指定して実行を表示 あのファイル名を指定して実行をプログラムから呼び出す。 2011/3/27
ファイルをダイアログを表示して処理 エクスプローラーのように、移動、消去、コピーをダイアログを表示して処理します。 2011/3/27
予約語を名前に使う intなどの予約語を変数やクラス、メソッドなどの名前に使う方法 2011/4/1
ILSpyの使い方 .NETのプログラムをC#に逆コンパイルできます 2011/4/30
デバッガビジュアライザ ブログで紹介したデバッガビジュアライザのリンク 2011/5/9
画像を高速で比較する 画像が同じかどうかを、高速で比較します。 2011/6/11,2011/9/28
列挙体を汎用的に扱う あらゆる列挙体を同じように処理できる関数などを作る際に。 2011/6/20
自作タイピングクラスの使い方 C#でタイピングげームを作る際に使えるクラスを無料公開しています。 2011/7/16,2011/7/20
日本語タイピングアルゴリズム? 日本語タイピングを作る際のアルゴリズムというか、メモです。ご参考までに。 2011/7/20

デザイナかどうか判断する

C#では一般的にフォームプログラミング中に、VisualStudioでデザイナを使います。
ですが、自作コントロールを作る場合などに、デザイナでは対応していない動作や、想定している動作ができない場合があります。 また、デザイナのときだけ実行したいときもたまにあります。
そういう場合には、Control.DesignModeを使うことができます。しかし、このプロパティ、完全に不良品です。
コントロールの中のコントロールや、コンストラクタでは常にfalseが返り利用できないなど、まともに使えません。
そこで、いくつかの方法でデザインモードかを判定できるメソッドを作ります。
戻り値
bool : デザイナならtrue
public static bool IsDesignMode()
{
   bool returnFlag = false;
   if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
   returnFlag = true;
     else if (Process.GetCurrentProcess().ProcessName.ToUpper().Equals("DEVENV") 
     || Process.GetCurrentProcess().ProcessName.ToUpper().Equals("VCSEXPRESS"))
   returnFlag = true;
     else if (AppDomain.CurrentDomain.FriendlyName == "DefaultDomain")
   returnFlag = true;
   
   return returnFlag;
}	
さらに、拡張メソッド化します。こうすればどのコントロールからもstaticでこのメソッドが利用できるようになります。
拡張メソッドはstaticクラスである必要があるため、別のクラスになります。それをコントロールを継承したクラスから使うテストコードが下です。
Testクラスをフォームアプリケーションで使ってみてください。
public static class ControlExtensions
{
    public static bool IsDesignMode(this Control ctrl)
    {
    bool returnFlag = false;
    if (System.ComponentModel.LicenseManager.UsageMode == System.ComponentModel.LicenseUsageMode.Designtime)
    	returnFlag = true;
	else if (Process.GetCurrentProcess().ProcessName.ToUpper().Equals("DEVENV")
    	|| Process.GetCurrentProcess().ProcessName.ToUpper().Equals("VCSEXPRESS"))
     	returnFlag = true;
    else if (AppDomain.CurrentDomain.FriendlyName == "DefaultDomain")
    	returnFlag = true;

	return returnFlag;
	}
}
    
public class Test : Control
{
    public Test()
	{
        if(this.IsDesignMode())
        MessageBox.Show("DesignMode");
            else 
		MessageBox.Show("Not DesignMode");
	}
}
	

予約語を変数名に使う

C#ではあらかじめ決められた予約語を利用することはできません。 たとえば、

int int = 10;   
MessageBox.Show(int.ToString());
などということはもちろんできません。これは入門者でも知っている基本中の基本です。
しかし、実は使う裏技があります。
変数名に@(逐語的識別子)をつけるのです。(ちなみに、文字列の前に@をつけて\をエスケープしないようにするのは逐語的リテラル文字列といいます)。
	int @int = 10;
	MessageBox.Show(@int.ToString());
	
こんなことが可能です。
VB.NETでは[Dim]のように[]で囲うことで可能です(こちらはエスケープ指定子といいます。) ただし、この書き方は法規的措置のようなものです。
コードの可読性を著しく下げるので、できる限り使わないようにしましょう。

何故こんな機能があるんだ!と思う方もいるでしょうが、それは.NETならではの理由です。 たとえば、VBではintというのは予約語ではありません。 なので、ライブラリの変数やプロパティ、メソッドの名前に「int」を使ってしまう可能性があるのです。 そうすると、C#からアクセスすることができなくなってしまいます。 そういうときに、この@を使うことで、予約語と同じ名前のプロパティなどにアクセスをすることができるのです。

イメージ図

この機能は、.NETで最低限の実装とされれる、CLS(共通言語仕様)でサポートする必要があるとされています。
予約語は言語によって違います。予約語はC#に合わせろというわけにはいきませんので、これは当然の実装なわけです。
これに100%従った書き方をすればほかの言語からでも普通に使うことができます。
これにはほかにも、ポインタを使ってはいけない、大文字小文字の違いしかない名前をつけてはならない、 uintなどの型はサポートされない、演算子のオーバーロードはできないなどのいろいろな制約があります。
かなり厳しいですが、実際は外部から見える部分(メソッドの呼び出しなど)にだけ気をつけて、実際の本体では使ってもかまいません。
また、uintの型なども対応していない言語は少ないでしょう。

下記のサイトに詳しくまとめられています。ライブラリを作る人なら、一度は見てみましょう。
言語間の相互運用性と共通言語仕様 (CLS)

ちなみに、VB.NETではAssemblyが予約語なので、System.Reflection.Assemblyをそのままでは使えません。
なので、Dim asm As [Assembly]となります。
これは、実際のMSDNでも使われています。Assembly クラス

Imports System
Imports System.Reflection
Imports System.Security.Permissions

Public Class LoadInvoke

     _
    Public Shared Sub Main(ByVal args() As String)
        Dim a As [Assembly] = [Assembly].Load(args(0)) 'ここ![Assembly]としている。
        Dim mytypes As Type() = a.GetTypes()
        Dim flags As BindingFlags = BindingFlags.NonPublic Or BindingFlags.Public Or BindingFlags.Static Or _
            BindingFlags.Instance Or BindingFlags.DeclaredOnly

        Dim t As Type
        For Each t In mytypes
            Dim mi As MethodInfo() = t.GetMethods(flags)
            Dim obj As [Object] = Activator.CreateInstance(t)

            Dim m As MethodInfo
            For Each m In mi
                ' Instead of invoking the methods,
                ' it's safer to initially just list them.
                Console.WriteLine(m)
            Next m
        Next t
    End Sub 
End Class 

	
JScript.NETや、J#、F#などのほかの言語も調べてみたのですが、この指定のしかたは見つかりませんでした。
実装されていないのか、それとも見つからないだけなのか。
もし知っている方がいましたらブログのコメントまでお願いします。

ILSpyの使い方まとめ

ブログの方で細かく説明をしていますが、こちらでさっぱりとまとめます。

インストール

まず、.NET4が入っていないなら、Microsoftのページ、.NET Framework ダウンロードから.NETをインストールします。
続いて、SharpDevelop Wiki - ILSpy(公式サイト) から最新のバイナリを入手してください。
上のほうにある、「Binaries」というのがそうです。
ZIPなので、解凍すればいくつかのファイルが出てきます。その中の、ILSpy.exeが本体です。
特にインストーラはないので、これをダブルクリックすれば起動します。

基本的な使い方

詳しく見る

左のツリービューに名前空間やクラスの一覧が表示されます。基本的なものは、Systemよりもmscorlibに含まれています。
ツリービューの上にあるテキストボックスにクラス名やメソッド名を入れると絞込みができます。
クラスや、その中のプロパティやメソッドをクリックすると右のエリアに逆コンパイルされたコードが表示されます。
右に出現したコードの中で、各種クラス、メソッド名をクリックするとそちらに飛んで表示してくれます。

[File]=>[Open from GAC]から、インストールされているライブラリ一覧を追加できます。
たとえば、System.Windows.Formsを追加したりできます。
[File]=>[開く]から、自作アセンブリを読み込めます。C#やVB.NETなどで作られたexeやDLLです。

逆コンパイルしたファイルを保存する際は、[File]=>[Save codes]を選択します。メソッドやクラスを選択すると.csファイルとして保存できます。
ツリービューのルートにあるアセンブリを選択してやると、その中をすべて保存できます。C#のプロジェクト形式になるのでVisualStudioで開くことも可能です。
ただし、mscorlibなどを保存しようとするとかなりの時間がかかるので注意。

また、リソースなども抽出できます。フォルダアイコンのResourceを開くと中に入っています。デザイナ用のアイコンなども入っています。
同じように[Save codes]でも保存できますし、右側に表示されるボタンからも保存可能です。

ILSpyでは、LINQやyield return、匿名メソッドなどにも対応しています。また、XAMLも逆コンパイルされます。
精度もなかなかのものです。



かなり適当に紹介しました。
もっと詳しい説明は私のブログ
か、
無償の逆コンパイラ「ILSpy」を利用するには?[C#]- @ITを参照してください!

デバッガビジュアライザ

デバッガビジュアライザは、VisualStudio2005から使える、デバッグの補助をする機能です。
変数の中身を見たり、書き換えたりできます。
DLLになっており、フォルダにコピーするだけでインストールは完了します。入れるだけでかなり便利になります。
拡張機能やマクロとは違い無料のExpress版でも利用することができます。
当サイトでも、Stringビジュアライザ、Colorビジュアライザ、Bitmapビジュアライザ、Enumビジュアライザを公開しています。
ソース付きなので、作る方がいましたら参考にしてみてください。(なるかわかりませんが)
StringビジュアライザとColorビジュアライザはサンプル程度ですが、Bitmapビジュアライザは実用を目指して作りました。
Enumビジュアライザもシンプルですが、個人的には使える代物だと思っております。
こちらよりダウンロードできます

===利用法===
環境によって変わりますが、以下のようなパスにダウンロードして出てきたDLLをコピーすることで利用できます。
インストール パス\Microsoft Visual Studio 10.0\Common7\Packages\Debugger\Visualizers
マイドキュメント\Visual Studio 2010\Visualizers
「Visual Studio 2010」や、「Common7」などの部分はバージョンによって変わるので、お使いのバージョンにあわせたフォルダに入れてください。
詳細は、下の基本的な作り方にあります。

私のブログ

私が書いたブログのまとめ。無駄に小分けにしてすいません。
デバッガビジュアライザとは
デバッガビジュアライザ基本的な作り方
デバッガビジュアライザを追加するのと同じことをする
デバッガビジュアライザで使う関数
デバッガビジュアライザを自動コピーする
実際にStringビジュアライザを作る!
Colorを書き換えるビジュアライザ
シリアライズできないクラスをデバッガビジュアライザでデバッグする
シリアライズできないクラスをプロクシクラスを通してデバッグ
プロクシクラスから置き換える
画像をいろいろできるデバッガビジュアライザ
列挙体のビジュアライザ

他のサイト

参考にさせていただきました。連絡も無く勝手にリンクさせてもらいます。ごめんなさい。 MSDN - ビジュアライザ
Bug Catharsis
覚書/C♯/ビジュアライザを自作する
Schimaの日記

画像を高速に比較する

C#で画像が同じかどうか比較しようとして、
//img1とimg2に比較したい画像があるとする if(img1 == img2) { MessageBox.Show("画像が一致しました!"); }else{ MessageBox.Show("画像は一致しませんでした。"); }
このようなコードを書きます。しかし、これでは常に「画像は一致しませんでした」と表示されてしまいます。
これは、C#の比較はオブジェクトの参照が同じかどうかを比較しているに過ぎないからです。
オブジェクトと参照の関係
なので、
Image img = Image.FromImage(@"c:\test.png"); img2 = img; if(img == img2) { MessageBox.Show("画像が一致しました!"); }else{ MessageBox.Show("画像は一致しませんでした。");
とした場合は一致しますが、
Image img = Image.FromImage(@"c:\test.png"); Image img2 = Image.FromImage(@"c:\test.png"); if(img == img2) { MessageBox.Show("画像が一致しました!"); }else{ MessageBox.Show("画像は一致しませんでした。"); }
とした場合、一致しません。
では、実際に比較するにはどうすればいいでしょうか。


一番簡単なのは、GetPixelを使う方法です。
このようなコードです。
bool Compare1(Bitmap img1, Bitmap img2)
{
    if (img1.Width != img2.Width || img1.Height != img2.Height) return false;
    for (int i = 0; i < img1.Width; i++)
    {
        for (int j = 0; j < img1.Height; j++)
        {
            if(img1.GetPixel(i, j) != img2.GetPixel(i,j))return false;
        }
    }
    return true;
}

private void button1_Click(object sender, EventArgs e)
{
	Image img = Image.FromImage(@"c:\test.png");
	Image img2 = Image.FromImage(@"c:\test.png");
	if(Compare1((Bitmap)img,(Bitmap)img2))
	{
	 MessageBox.Show("画像が一致しました!");
	}else{
	 MessageBox.Show("画像は一致しませんでした。");
	}
}


Imageの場合は、Bitmapにキャストして渡す必要があります。
総当りで、全部のピクセルが同じかどうかを確認しています。
しかし、これには致命的な欠点があるのです。

高速にやるには?

GetPixelを使うのはあくまで簡易的な方法。
スクリーンショットなどの大きな画像を比較してみてください。
私の環境では、3秒ほどかかりました。
1枚の比較ならたいしたことはありませんが、100枚など比較するとなると、とんでもない時間がかかります。
また、もっと大きい画像を比較しても時間がかかります。
原因はGetPixel。GetPixelとSetPixelは便利ですが、とにかく遅いのが欠点なのです。

これをもっと高速にやるには、他の方法を使う必要があります。
詳しい解説はブログのページでやるとして関数を書きます。
bool ImageComp(Image image1, Image image2)
{
    Bitmap img1 = (Bitmap)image1;
    Bitmap img2 = (Bitmap)image2;
    
    //高さや幅が違えばfalse
    if (img1.Width != img2.Width || img1.Height != img2.Height) return false;
    //LockBitsでBitmapDataを取得
    BitmapData bd1 = img1.LockBits(new Rectangle(0, 0, img1.Width, img1.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, img1.PixelFormat);
    BitmapData bd2 = img2.LockBits(new Rectangle(0, 0, img2.Width, img2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, img2.PixelFormat);
    if ((bd1.Stride * img1.Height) != (bd2.Stride * img2.Height)) return false;
    int bsize = bd1.Stride * img1.Height;
    byte[] byte1 = new byte[bsize];
    byte[] byte2 = new byte[bsize];
    //バイト配列にコピー
    Marshal.Copy(bd1.Scan0, byte1, 0, bsize);
    Marshal.Copy(bd2.Scan0, byte2, 0, bsize);
    //ロックを解除
    img1.UnlockBits(bd1);
    img2.UnlockBits(bd2);
	
	//MD5ハッシュを取る
    System.Security.Cryptography.MD5CryptoServiceProvider md5 =
        new System.Security.Cryptography.MD5CryptoServiceProvider();
    byte[] hash1 = md5.ComputeHash(byte1);
    byte[] hash2 = md5.ComputeHash(byte2);
    //比較
    return hash1.SequenceEqual(hash2);
}
*コードに間違いがあり、ご指摘によって修正しました。ご迷惑をおかけしました。
たぶん、これが早い実装です。
LockBitを使ってBitmapDataを取得。それをMarshal.Copyを使ってbyteを配列にコピーします。それからMD5ハッシュをとって、比較します。
MD5ハッシュを取らずにすべて比較してもいいのですが、ハッシュを使ったほうが早いようでした。
最後の部分はいちいち変数に格納しなくてもできますが、この程度ならコンパイラが最適化してくれると思います。
このようにすると、私の環境ではGetPixelを使った場合に比べて30倍程度の違いが出ました。
この差は画像の大きさが大きくなればなるほど広がります。
縦横を2倍にした場合、GetPixelでは4倍、こちらの場合は2倍です。


ちなみに、SequenceEqualは、System.Linq.Enumerableで定義されている拡張メソッドで、配列が同じかどうか比較します。
使わない場合は、総当りで比較してください。

おまけ。拡張メソッドにする

C#の機能で、拡張メソッドにできます。拡張メソッドにすれば、Imageのインスタンスから比較するImageを指定して簡単に比較ができます。
こんな感じにしてください。
public static class ImageEx
{
    /// 
    /// 画像を高速に比較します。
    /// 
    /// 比較する画像
    /// 
 static public bool ImageComp(this Image image1, Image image2)
    {
        Bitmap img1 = (Bitmap)image1;
        Bitmap img2 = (Bitmap)image2;

        if (img1.Width != img2.Width || img1.Height != img2.Height) return false;
        BitmapData bd1 = img1.LockBits(new Rectangle(0, 0, img1.Width, img1.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, img1.PixelFormat);
        BitmapData bd2 = img2.LockBits(new Rectangle(0, 0, img2.Width, img2.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, img2.PixelFormat);
        if ((bd1.Stride * img1.Height) != (bd2.Stride * img2.Height)) return false;
        int bsize = bd1.Stride * img1.Height;
        byte[] byte1 = new byte[bsize];
        byte[] byte2 = new byte[bsize];
        Marshal.Copy(bd1.Scan0, byte1, 0, bsize);
        Marshal.Copy(bd2.Scan0, byte2, 0, bsize);
        img1.UnlockBits(bd1);
        img2.UnlockBits(bd2);

        System.Security.Cryptography.MD5CryptoServiceProvider md5 =
            new System.Security.Cryptography.MD5CryptoServiceProvider();
        byte[] hash1 = md5.ComputeHash(byte1);
        byte[] hash2 = md5.ComputeHash(byte2);
        
        return hash1.SequenceEqual(hash2);
    }
}
// 使い方:img1とimg2に画像が入っているとする。
// if(img1.ImageComp( img2 )){ MessageBox.Show("一致") }
こうしておけば、再利用が手軽になります。
[参考]
C#でBitmapの比較

列挙体を共通して扱うには

列挙体を扱うデバッガビジュアライザを作っていたのですが、結構難しくてはまったところがありました。
デバッガビジュアライザでは、列挙体を基本型のEnumとして受け取るのですが、その後の処理が難しい。
フラグなどを扱う場合は特にビット演算などを利用することになるわけですが、Enumではビット演算できません。
なので整数型に変換しますが、int以外が基本型に指定されている場合は問題が生じます。
それでいろいろと解決法をまとめておきます。

参考URL

  1. enum (C# リファレンス) - MSDN
  2. System.Enum - MSDN
  3. 列挙型 - ++C++;// 未確認飛行 C
そのほか、MSDNの下に書いてある関連項目を読むこともオススメします。
列挙体の基本的なデザインの仕方などが書いてあり、結構ためになります。

列挙体の基本

使い方などは上のサイトをごらんいただくとして、ちょっと特殊なことを。

(1)すべての列挙型は、System.Enumを継承します。

Enumは、すべての列挙体の基本型です。すべての列挙体はこれを継承しているのです。
(ちなみに、直接System.Enumを継承するべきではありません。)
なので、すべての列挙体の変数は、System.Enum型の変数に代入するとアップキャストとなり、暗黙的に方変換することができます。 逆の場合はダウンキャストとなるので、もちろん明示的に変換する必要が出てきます。

(2)System.Enumの静的メソッドで各種列挙体の操作をできる

列挙体を書いて、「.」を打つと、開発ソフトのインテリセンスが表示されますが、その中には列挙体の中身の一覧しかありません。
また、インスタンスもobjectと同じメソッドしか提供しません。
そのため、System.Enumにあるstaticメソッドを利用します。
詳しくはMSDNを。

(3)基本型はint以外もある

あまりないのですが、時々あります。
上のMSDNを見ていただければわかりますが、byteやulongなどの整数型(charを除く)をとることができます。
MSDNには、基本的にintをとるべきとしていますが、例外として、
(1)32bitに入りきらない場合
(2)アンマネージとのやり取りの都合
(3)頻繁にインスタンス化する構造体やクラスが持つ、大きな配列やコレクションに使われる、大量をシリアライズする
の場合にのみ別の基本型を使うべきだとしています。
それ以外の場合には大してメモリなどに影響はないので、定義するときはint型にしておきましょう。

それでも、外部では使われていることがあります。なので、単純にintにキャストを行うと例外がおきます。
int以外の型変換のテストも必ず行いましょう。

(4)フラグとして扱う場合はFlagsAttribute属性をつける

列挙体をフラグとして扱う場合、System.FlagsAttribute属性を指定するべきです。
指定しなくてもフラグのように扱うことはできるのですが、ToStringの結果や、開発環境、一部のツールで違いがでます。
詳細は以下。 FlagsAttribute - MSDN

汎用に扱うには? - 値を整数型にする

汎用的に扱うとき、(3)を見ていただけるとわかりますがintにキャストして扱ってはいけません。
ではどうすればいいのか?
こちらのMSDNをどうぞ。整数型の一覧表 この中でchar以外が指定できます。
すべての整数の値を保てるのは、longかulongです。
ここでは、longに変換して使います。
では、ためしにこんなコードをそれぞれ描いてみてください。(同時にではなく、別々に)。FontStyleは、System.Drawing.FontStyleです。FontStyleの基本型は、intです。

//サンプル1
FontStyle fs = FontStyle.Bold;
long test = (long)fs;
MessageBox.Show(test.ToString());

//サンプル2
FontStyle fs = FontStyle.Bold;
Enum testenum = fs;
long test = (long)testenum;
MessageBox.Show(test.ToString());

//サンプル3
FontStyle fs = FontStyle.Bold;
Enum testenum = fs;
long test = (long)testenum;
MessageBox.Show(test.ToString());
	
サンプル1では、ちゃんと1と表示されると思います。
しかし、汎用受け取るには、すべての列挙体の継承元である、Enum、ValueType、objectのいずれかで受け取る必要があります。
そのEnumで受け取ったのがサンプル2です。
しかし、「型 'System.Enum' を型 'long' に変換できません。」とでてコンパイルできません。

ということでobjectにしたのがサンプル3です。
コンパイルはちゃんと通ります。しかし、実行時に例外。「指定されたキャストは有効ではありません。」という例外が。

型が違うため、アンボックスに失敗するようです。
(詳細はInvalidCastExceptionを参照。)
その結果、私が導いたのは、整数型の数は知れているからTypeで判断して条件分岐させよう!でした。
しかし、その考えを実現させるとコード量が多くなり、ジェネリックにまで手を出したものの簡単ではありませんでした


しかし、実際はもっと簡単にやる方法があったのです。
それは、Convert.ToInt64を使うことです。これを使えばObjectも一発でlongに変換できます。(ToInt64に気づく前に、Convert.ChangeTypeを使っていた、というのは秘密です)
以下がサンプルです。ここではEnumの変数ですが、どの道objectにアップキャストされることになるので、objectにしても同じです。
FontStyle fs = FontStyle.Bold;
Enum testenum = fs;

long test = Convert.ToInt64(testenum);
MessageBox.Show(test.ToString());
	
うまくいっていると思います。独自の列挙体を指定してみてもうまくいくことがわかると思います。
これを使ってlongにすれば、ビット演算も足し算も比較もし放題になります。

汎用に扱うには? - 整数型を列挙体型に戻す

では、逆に変換した整数型を列挙体に戻します。

//##### サンプル1 #####
FontStyle fs = FontStyle.Bold;
Enum testenum = fs;

long test = Convert.ToInt64(testenum);

FontStyle fs2 = (FontStyle)test;
MessageBox.Show(fs2.ToString());


//##### サンプル2 #####
FontStyle fs = FontStyle.Bold;
Enum testenum = fs;

long test = Convert.ToInt64(testenum);

object temp = test;

//以下は渡す側の処理。なのでobject型で帰ってきた値を直接変換しようとする

FontStyle fs2 = (FontStyle)temp;
MessageBox.Show(fs2.ToString());


//##### サンプル3 #####
FontStyle fs = FontStyle.Bold;
Enum testenum = fs;

long test = Convert.ToInt64(testenum);

object temp = Convert.ChangeType(test,test.GetType());

//以下は渡す側の処理。なのでobject型で帰ってきた値を直接変換しようとする

FontStyle fs2 = (FontStyle)temp;
MessageBox.Show(fs2.ToString());	
	
サンプル1はやはりちゃんと動きます。しかし、列挙体すべてを汎用的に扱うということはFontStyleというキャストは行えません。
Enumかobjectで渡す必要があります。
(もちろん、これは関数などの内部処理になるはずです。その関数に列挙体を渡す側は定数で持っているので、普通に型変換できます。なので、EnumかObjectにできればよいのです)
では、そのままobjectで返してみたのが2です。
つながっていますが、コメントの部分で別れていると思ってください。
コンパイルは通りますが、やはり例外がおきます。もちろん、fsを直接objectに入れればいけますが、それなら戻り値などいらぬ。
long型に変換した後で書き換え処理があったとします。
ということで、さらにChangeTypeで動的に型変換を行ったのがサンプル3です。
しかし、これでもだめ。同じように例外が出ます。

ココでは書いてありませんが、Enumにキャストしようとしても、直接もサンプル3のChangeTypeを使ってもそれでも例外がおきます。


では、どうすればいいのでしょうか。
答えは意外に簡単でした。
Enum.ToObjectを使うのです。
FontStyle fs = FontStyle.Bold;
Enum testenum = fs;

long test = Convert.ToInt64(testenum);

object temp = Enum.ToObject(fs.GetType(), test);

//以下は渡す側の処理。なのでobject型で帰ってきた値を直接変換しようとする

FontStyle fs2 = (FontStyle)temp;
MessageBox.Show(fs2.ToString());
    
悩んだのがうそのように簡単です。ほんとに。だからMSDNはちゃんと確認しなきゃだめなんですね・・・反省。
これで、整数型への変換と、列挙体型への変換はできました!!

おまけ

ついでに、もう少しつまりそうなところを。
ちなみに、Typeは列挙体の型を指定して、typeof( FontStyle )のようにするか、 インスタンス.GetType() のようにして取得できます。

(1)すべての値、すべての名前の列挙

Enum.GetValuesや、Enum.GetNamesを使います。GetValuesだと値が、GetNamesだと名前が取得できます。
GetValuesで取得した値のToStringを呼んだり、GetName(GetNamesではなく)を呼べば名前も入手できます。
逆に名前から値を取得する場合、Parseでできます。しかし、実験はしていないものの遅そうです。
名前だけならGetNames、値も必要ならGetValuesにしましょう。

(2)列挙体の基本型を取得する

あまり聞かないような名前のメソッドですが、Enum.GetUnderlyingTypeを使います。
これをやれば、Int32(int)や、Byteといった型のTypeが取得できます。

(3)FlagsAttributeが付いているかの判断

Attribute.IsDefinedを使います。
void Test(Enum obj)
{
    if (Attribute.IsDefined(obj.GetType(), typeof(FlagsAttribute)))
    {
        MessageBox.Show("Flags属性が適用されたフラグ列挙体です");
    }else{
        MessageBox.Show("Flags属性が適用されていない、普通の列挙体です");
    } 
}
		

まとめ!

(1)ビット演算などもしたいなら、Convert.ToInt64でlong型に変換して扱うべし!

(2)逆に整数型から元の列挙体に戻したいならEnum.ToObjectを使うべし!

これだけですね。
近日中に、列挙体のデバッガビジュアライザをソースつきでリリースする予定なので、よければそちらもどうぞ。

タイピングクラスの使い方

私が作った、日本語タイピングをするためのクラスです。
こちらもあわせてご覧ください。「C#で日本語タイピングができるライブラリを公開(ブログ)


その前に略語的なものの解説。 「音節」正しいのかはわかりませんが、ローマ字入力で「ち」「ちゃ」「っちゃ」などの一度に入力できるものです。
「音節入力」音節を入力することです。たとえば、「ちゃ」なら[CHA]が音節入力です。わかりにくいですが、適当に流してください。
「キー定義」日本語に対応するローマ字の一覧です。XMLファイルから読み込みを行います。

ダウンロード

こちらからダウンロードできます。
ライセンスはMITとなっています。私の名前やサイト名をどこか片隅に書いてくだされば改変、再配布も含めて自由です。
ジオシティーズの制限上、.csのファイルをそのままアップロードできないので、拡張子を.txtに変えてあります。
GUIタイピングについてはこちらをご覧ください。

本体
キー定義
CUI版の簡易サンプル
GUI版の簡易サンプル
上の3つをまとめたもの ↑旧版(ver1) typing.csの225行目あたりで、最後が「ん」で終わるときに例外が発生してしまうようです。
以下のように修正してみてください。
(修正前)if (question[loc + 1] == 'ん') dic.Add(2, new string[] { "NXN" });
(修正後)if (question.Length > (loc + 1) && question[loc + 1] == 'ん') dic.Add(2, new string[] { "NXN" });
ブログのWeb拍手コメントでご指摘いただきました。ありがとうございました。遅くなってすいませんm(__)m
なお、zipの中身はまだ変更していないです・・・ごめんなさい。

基本的な使い方

InputResult列挙体

None 無効な値です。これが戻ってくることはありません
Good 正しい入力です。ただし、CharEnd、CharEndN、CharEndNN、StrEndはこれより優先されます。
Bad 間違った入力です。
CharEnd 音節または記号の入力が終わりました。「あ」「ちゃ」「っしゃ」「。」などもこれが戻ります。1文字ではないので注意してください。何文字入力されたかは、Locationで位置をいろいろやればいいと思われます。
CharEndN CharEndと同じですが、次の[N]の入力は無視されます(UserInputなどには含まれます)。「きんこ」の場合、[KINKO]と[KINNKO]の両方を入力できるようにするための措置です。
CharEndNN CharEndNの次にもう一度[N]を入力した場合に戻ります。CharEndと区別するためにあり、この後の入力で特殊な判定がなされることはありません。
StrEnd 問題文をすべて入力し終えました。新しい問題文を設定するなり、ゲームを終了するなりの措置をとってください。これは他の戻り値よりも優先して戻されます。

Typingクラスメンバ一覧

[Public]

・プロパティはすべて読み取り専用です。
メソッド SetQuestion 現在入力中のデータを破棄して、新しい問題文を設定します。
メソッド InputResult Input(char) 入力を行います。半角の文字を指定します。大文字小文字は区別されません。
メソッド Dictionary<int,string[]> GetKohoList(int) 指定した位置の文字の入力候補の一覧を取得します。Dictionary型で返されます。
ひらがな1文字分の入力(「あ」「か」など)は、Keyが1。ひらがな2文字分(「ちゃ」「った」など)はKeyが2。 ひらがな3文字(「っちゃ」「っぴょ」など)も同様に3です。
valueにあたる文字の配列は、「ち」の場合、[TI][CHI]という配列になります。
この関数では、次の文字も考慮され、「ん」の場合の[N]の繰り返しの考慮などもされています。
staicメソッド string QuestionCheck(string) 問題文が正しいかを確認します。正しい場合、nullが帰ります。間違っている場合、その間違っている文字列が返されます。もし渡された文字列が空文字やnullの場合、空文字が帰ります。
プロパティ string QuestionText 入力中の問題文のひらがなです。
プロパティ string UserInput ユーザーが実際に入力したアルファベットです。大文字になっています。
プロパティ string RomaKoho この先の入力の候補です。現在は仮実装となっており、結果を制御することはできません。UserInput + RomaKohoで、完全なローマ字の文字列となります。 この関数では、現在までに入力されたアルファベットも考慮されます。「ちゃ」で[C]と入力すると、[TYA]ではなく[CHA]や[CYA]がでます。
プロパティ string AlreadyInput 既に入力された文のひらがなです。
プロパティ string RemainInput まだ入力されていないひらがなです。現在入力中の文字もココに含まれます。
プロパティ int InputPosition ひらがなの入力位置を示します。
定数 RegexPattern 判定法の変更により廃止。QuestionCheckメソッドを利用してください。

[Private]

特殊な理由がない場合、変更することは推奨されません。
改造したい場合のためにメモとして残しておきます。
メソッド(static) void LoadKeyList Keysフィールドにキー定義を読み込みます。Linq To XMLを利用した非常に簡単な実装となっています。
フィールド string question ひらがなの問題文
フィールド int location ひらがな問題文の現在の入力位置
フィールド string userInput ユーザーが実際に入力したアルファベット
フィールド int charLocation 音節入力の現在の位置を取得します。「ちゃ」の入力で[CH]まで打ってあったら[2]です。
フィールド string charInput 音節入力で現在入力された値です。[CH]とかが入ります。
フィールド bool NFlag これがtrueの場合、一度だけ[N]の入力が許容されます。
フィールド(static) Dictionary Keys キー定義はこのフィールドに読み込まれます。
定数 string wtable キー定義に含まれない全角半角英数字、記号などの入力を拡張します。日本語とちがい、1文字で入力できるもののみを単純に対応させて入力を可能とします。句読点などの処理も行っています。
定数 string stable wtableに対応する半角の入力データです。wtableの同じ位置にある英数字記号が入力できるようになります。たとえば、「0」に対応するのは[0]ですし、「+」に対応するのは「+」です。

キー定義のXML

キー定義のXMLは非常に簡易的なものです。 <Key Char="あ" Roma="A" /> <Key Char="い" Roma="I,YI" /> <Key Char="ちゃ" Roma="TYA,CHA,CYA" /> のように、Charに日本語を、Romaにそれに対応するアルファベットを大文字で入れます。複数ある場合は,で区切ります。 (XMLなのに、「,」で区切るのようなことをするのはあまりよいことではないことは理解していますが、たいしたことはしないのにXMLが複雑になりすぎることを避けたかったのです。)

日本語タイピング用アルゴリズム(?)

私が作った際の考え方やメモ、注意点をまとめておきます。
作る方はぜひちょこっと覗いていってください。ただし、完全に信用すると失敗するかもしれないので、他のサイトなどもしっかりと見てくださいね・・・
あと、前のTipsの「日本語タイピングクラスの使い方」もご確認下さい。
一部そのときに使った用語や、それを参照して、といっている部分があります。

ネットなどでもよく公開されているタイピングゲームをC#で作ります。
英字版は非常に簡単にできます。(C#で英字タイピングソフトを作る
しかし、日本語に対応させるとなると結構難しかったりします。

その理由は、

  1. 複数の打ち方がある。
    例:「つ」 => [TU][TSU]
  2. 2文字以上を同時入力できる。
    例:「ちゃ」 => [CHA][TYA][CYA]、「っじゅ」=>[ZZYU][JJU]
  3. 促音(っ)が存在する。
    例:[った] => [TTA][XTU TA][LTU TA]
  4. 「ん」の特殊な入力システム
順番に説明します。

(1)複数の打ち方

これはわかりやすいと思います。
「つ」の場合、[TU]と、[TSU]の2種類の打ち方があります。

(2)2文字以上の同時入力

「ちゃ」の場合、[CHA][TYA][CYA]のどれかだと、まとめて2文字入力することができます。
もちろん、[TI XYA][CHI XYA][TI LYA][CHI LYA]と分けて打つこともできるので、(1)の問題もあわせて「ちゃ」だけで7通りもあります。

(3)促音の存在

「った」の場合、[TTA][XTU TA][LTU TA]の3種類の打ち方ができます。
このように前を重ねて促音を現せるのは、「あいうえおなにぬねのん」の11文字(ただし、「ん」については(4)で)以外です。
これは難しそうですが、実際にやってみるとたいしたことはなかったりします。
たぶん、やり方によっては(2)の問題のみになります。

(4)「ん」の特殊な入力システム

これが一番厄介といえるでしょう。「ん」の特性は以下です。

最後のものは、(3)にも関与しますね。
あるうち方ではダメだけど、違ううち方だといい、というかなり面倒な設定をしてやる必要があります。
少なくとも、プログラム側で別途処理を行う必要が出てくることでしょう。
「んん」だの「っん」だの打つことねーよと思うかもしれませんが、これから何があるかわかりません。対応させておくが吉です。

日本語入力では固定式と、あいまい式があります。(命名は私--;)
固定式は『「つ」は[TU]のみ![TSU]はできません!』とする方式です。しかし、これは不便。
あいまい式は『「つ」は[TU]も[TSU]もOK!』とする方式です。
プログラムとしては、言うまでもなく固定式が簡単です。
しかし、固定式のタイピングゲームは極力避けるべきです。私がタイピングゲームを探して、固定式しかないゲームがあったら、それは即効で候補から落とします。
ということで、あいまい式を作る際の作り方、ポイントを紹介します。

[1]カプセル化する

これを最初にもってくるのはあれかもしれませんが、途中から変えるのは難しいので。
タイピングゲームなら、最終的にGUIにしたいと思いますが、GUIはデバッグが難しいです。
C#やVB.NETなどのオブジェクト指向言語では、タイピングを管理するクラスをカプセル化して、単独で動くようにしてください。
そして、開発はコンソールアプリケーションで動かしましょう。
完成後に、クラスをそのまま新しいプロジェクトに持っていけば後は他の部分の開発に専念ができます。

[2]日本語入力定義ファイルを作る

日本語入力で、「つ」=>[TSU][TU]などの入力のしかた一覧をどこかに用意する必要があります。
リテラルとしてプログラムに組み込んでもよいのですが、保守も考えると外部ファイルが便利かもしれません。
タイピングゲームならそれほどロード時間を気にすることもないでしょう。
C#で扱うなら、XMLにして、LINQ to XMLを使うと非常に簡単です。

ただし、私の場合英数字記号はXMLに入れないほうが効率的と考えたので、処理しています。
定義ファイルに入れてしまいました。定義ファイルは大きくなりますが、この方がわかりやすいと思われます。
ユーザーが変更もできるようになるので有利です。
ただし、こうする場合、私のように「,」を区切りとして使っている場合は「,」が候補の場合に特別処理をしてあげる必要があるので注意してください。

[3]定義ファイルは連想配列に読み込む

[2]の定義ファイルをメモリに読み込みます。
私の場合、連想配列(Dictionary)に読み込むことにしました。
これのキーの型はstringにします。値の型は、string[](stringの配列)にします。
そして、キーのほうにはたとえば、「あ」とか、「ちゃ」とかが入ります。
値のほうにはそれに対応するローマ字です。
たぶん、このようにするのが一番扱いやすいと思います。

英数字記号を別途用意した場合、ココで候補リストに入れ込んでしまったほうがいいです。

[4]問題文設定

問題文を受け付ける際は問題文に想定外の文字が含まれていないかを確認すべきです。
正規表現を使うと簡単です。
想定外の文字のときは、例外を出すのがいいと思われます。

[5]入力できるローマ字一覧を取得

続いて、問題文を元に入力できるローマ字一覧を取得します。(私はGetKohoListという変な名前にしてしまいました)
このときに、「ち」なら[CHI][TI]を取得します。しかし、その次に「ゃ」があった場合[CHI][TI]のほかにも[TYA][CYA][CHA]を想定しなくてはなりません。
また、「っちゃ」の場合、最大3文字まで同時入力される可能性があります。

どのように実装するかはあなたしだいですが、私の場合はまたもや連想配列を利用しました。
今回は、Dictionary<int,string[]>型です。
キーになるint型は、ひらがな何文字分かです。そのデータは、そのローマ字です。
「っちゃ」でいくと、
1 => っ => XTU,LTU
2 => っち => TTI,CCHI
3 => っちゃ => TTYA,CCYA,CCHA
という具合です。
この関数を作る際の注意ですが、現在の入力位置を想定するだけではなく、後々入力候補を取得したい機会が出てくる可能性が高いです。
そのために、任意の位置の候補を取得できるようにしておくことをオススメします。

また、この関数では、「ん」の仕様を解決させましょう。
・「ん」が文字列の終わりなら[NN]または[XN]
・「ん」の次の文字が「あいうえおなにぬねの」のいずれかなら[NN][XN]
・それ以外なら[N][XN]ただし、[N]の場合は次の入力で[N]が許されるようにしなければならない。これはこの関数の外で実装するべき
・「ん」の次の文字が「ん」なら[NN][XN]。または[N]で次の「ん」は[XN」。すなわち、[NXN]で「んん」になる。これもこの関数単体では難しい

[6]入力する関数

前のTipsに書いてありますが、一応コピー。私が使っている用語です。
「音節」正しいのかはわかりませんが、ローマ字入力で「ち」「ちゃ」「っちゃ」などの一度に入力できるものです。
「音節入力」音節を入力することです。たとえば、「ちゃ」なら[CHA]が音節入力です。

入力は、半角の文字のcharで受け取ります。
戻り値として、入力の結果を書きます。私の場合は、前のTipsの日本語タイピングクラスの仕様に乗っている列挙体を使います。
以下、その列挙体の名前を使わせていただきます。

まず、アルファベットは大文字小文字を区別しないよう、最初にChar.ToUpperなどを使っておくことをオススメします。 続いて、候補一覧を取得します[5]の関数を使います。
そして、それをforeachなどで列挙させます。
入力候補一覧のうち、ユーザーが入力した文字とマッチするものが存在するかを確認します。
もしあった場合、入力成功です。
入力成功した際には、以下のように処理をしておきます。
・全部のローマ字を取っておくフィールドに今のものを追加。
=>これは、ユーザーがなんと入力したかを表示するために使われます。
・現在の音節入力の入力データに今入力されたものを追加。「ちゃ」の場合[CH]など
=>これは、候補を絞り込むのに使われます。StartWithなどを使って判定する候補を絞り込みましょう。
ここで、音節の入力が終わっているかを確認します。
もし終わっていれば、ひらがなの入力位置を動かします。私が作ったのだと、locationです。「ち」の場合は1、「ちゃ」の場合は2文字ずらしましょう。
さらに、ココで「ん」の仕様を九州します。もし、「ん」の入力で[N]一回だけの入力の場合、次にもう一度だけ[N]を入力することもできます。(しないこともできます。)
フィールドでフラグを作っておいて、この関数の最初でフラグがたっていて入力がNの時はすぐにCharEndNNを返すようにすればいいと思われます。
ちなみに、フラグを使う際は注意してください。下手なタイミングで解除すると、正常に動かなくなります。たとえば、次にNを押せる場面で一度入力をミスした時にNが入力できなくなるようなわかりにくいバグが生まれます。


さらにそのとき、文字列の最後か確認して、最後ならStrEndを戻します。
また、音節の終わりならCharEndを戻します。次にNが入力できる際はCharEndNにします。
そして、どれにも当てはまらない場合Goodを戻します。


ココの関数の説明は私が説明べたなのでわかりにくいですが、実際のソースを見てみると意外と簡単です。

書き忘れましたが、スペースを抜かして処理する際もココで処理を行うほうがいいです。

[6]必要な分を実装して完成

あとは、問題文や入力済みのひらがななどを公開するプロパティを付けたら一応完成です。
こちらのテストパターンを一通りチェックしてみるとバグがないかとりあえず確認できます。



とりあえず、こんなものでしょうか。わかりにくくてスイマセン・・・。
ここで紹介したのはあくまで一例なので、もっといい方法がたくさんあることでしょう。
もしわかりにくいことがあれば、時間とやる気があればある程度お答えします。