C#3.0の拡張メソッドを使って As とか Switch/Case とかを作ってみた
As メソッドの巻
次のようなコードを書く機会が多い。obj が Hoge インスタンスかどうかを判定して、Hoge インスタンスの場合のみ何か特別な処理をしたいのだ。
Hoge hoge = obj as Hoge; if ( hoge != null ) { ... }
しかし、どうもこのコードが気に入らない。変数 hoge のスコープが気に食わないのだ。hoge は if 文の中でしか使わないのだから、if 分のスコープに閉じ込めてしまいたい。
そこで is 演算子を使って次のように書いてみる。
if ( obj is Hoge ) { Hoge hoge = (Hoge)obj; ... }
確かにスコープの問題は解決した。だけどやっぱり気持ち悪い。Hoge インスタンスかどうかの判定が重複しているじゃないか。
というわけで、やっぱり as 演算子に戻して次のように書いてみる。
{
Hoge hoge = obj as Hoge;
if ( hoge != null ) { ... }
}
こうすればスコープの問題もOKだし判定も1回だけなのだけれど、さすがに見た目がすっきりしない。うーんうーんと悩んだ末に、次のような拡張メソッドを作ってみた。
public static void As<T>(this object self, Action<T> action) where T : class { T arg = self as T; if (arg != null) action(arg); }
コレを使えばこう書ける!
obj.As<Hoge>( hoge => { ... } );
Switch/Case の巻
次のようなコードを書く場面も結構ある。
if ( obj is Foo ) { Foo foo = (Foo)obj; ... } else if ( obj is Bar ) { Bar bar = (Bar)obj; ... } else if ( obj is Buz ) { Buz buz = (Buz)obj; ... }
いやー、やっぱりイマイチです、このコード。そこでまず、次の Switch クラスを用意して・・・
class Switch { readonly object _self; bool _done = false; public Switch(object self) { _self = self; } public Switch Case<T>(Action<T> action) where T : class { if (_done) return this; T cast = _self as T; if (cast != null) { action(cast); _done = true; } return this; } }
次の拡張メソッドを用意すれば・・・
public static Switch Switch( this object self ) { return new Switch( self ); }
こう書ける!
obj.Switch()
.Case<Foo>( foo => { ... } )
.Case<Bar>( bar => { ... } )
.Case<Buz>( buz => { ... } );
とはいえ・・・
object に拡張メソッドを定義するのはちょっと気が引けるなぁ。皆さんどう思います?