雑記C#

EnumをLInqで使いたい!

列挙体とLinq

C#のLInq!便利ですよね^^
気をつけないとなんでもLinqで書きたい中毒者になってしまいそうですww

今回は列挙体をLInqで使う時に少々ハマったのでメモしておきます!

列挙体

「列挙体」と入力したいのに「列挙隊」と変換されるMacに少しイライラww

まぁ…それは置いといて列挙体の解説はこの記事を読んでいる方は大丈夫ですよね^^

こんな感じで動物の列挙体を定義したとして…

このように鳴き声を取得する関数などを作った時に可読性が良くなります。
※switch式使えば?という話はとりあえずおいといて^^

Enumで定義されている値を全取得

では、4種類全ての動物を取得したい場合はどう書けば良いのでしょうか?
列挙体で定義されている値を全て取得したいということですね。

Array Enum.GetAllValues(Type type); 
上記メソッドを使えば列挙体で定義されている値を全て取得できます。typeに列挙体の型を渡すことで列挙体に定義されている全ての値をArray型(配列)で取得できます。
つまり…

このようにすればAnimal列挙体の全ての値を取得できるわけですね!

全ての動物の鳴き声を取得する

前節で、列挙体に定義されている全ての値を取得することができたので後は簡単!

上のようにすれば全ての鳴き声が取得できる~…とはいきませんww

気づいている方も多いはず…Enum.GetValuesメソッドで返されるオブジェクトはArray型なんです。実はこのArray型、非ジェネリック型です。実はiの型はobject型なんです。
public static string GetCryText(Animal animal); 
と定義したのに、引数にobject型を渡していればエラーが出ますよね?

さっき成功したのはi.ToString()としていたから…たまたまobjectのメソッドを使っていたから上手くいっていただけなんです。

つまり上記のようにAnimal型にキャストしてあげないと上手くコンパイルが通りません。

LInqを使いたい!

前置きが長くなりましたがここからが本題です

今まで列挙体で定義されている値を全て取り出してコンソールに出力する…ということをやってきた訳ですが、見出しの通り「Linqが使いたい」場合にどうするか考えてみます。

Linqを使うメリットは?

「なぜLInqを使うの?」という疑問が浮かぶ人もいるかもしれません。もちろん前述したような列挙体の値を全て出力するだけなら前述の方法でも構いません。

例えば「Animal型に定義されている動物の、鳴き声が3文字のものだけをList<string>として保存したい」のような場合はどうでしょうか?

今までのやり方だと上記のようなコードになるでしょう。
でもLinqを使って…

上記のようにかけたら気持ち良くないですか?とてもすっきりと書けますよね?もちろん上記はエラーとなりますが、このように書きたいと思ったんです!

Array型はIEnumerable<T>を実装していない

前述の通りArray型は非ジェネリック型…つまりIEnumerableインターフェースは実装していますが、IEnumerable<T>インターフェースは実装していないんです。

しかし、LinqのほとんどはIEnumerable<T>の拡張メソッド群であり、IEnumerableには使えないメソッドが多いんです。Selectメソッドもその一つです。

Cast<T>()メソッドって使えないの?

私がまず一番に考えたのがCast<T>()メソッド…

Cast<Animal>()と書けばIEnumerable<Animal>に変換してくれるだろう…とか考えたのですが、実はCast<T>()メソッドもIEnumerable<T>型の拡張メソッドなんです…(T_T)

なので、上記のようなコードを書いてもCastメソッドが見つからない的なエラーでコンパイルエラーとなります。

「ではどうすればいいんだ?」自作拡張メソッドなど、いろいろ手を考えましたがスマートな方法が見当たらない…と思ったその時!「あった~!ドンピシャ~」というメソッドを見つけました^^

結果:OfType<T>()メソッドが正解?

OfType<T>()メソッド!見たときはCast<T>()と何が違うの?…とか思いましたww

CastメソッドはIEnumerable<T1>の要素全てをキャストしてIEnumerable<T2>のように変換してしまうメソッドです。T1からT2にキャスト出来ない場合は例外を出します。

対してOfType<T>()メソッドはIEnumerableの全ての要素から型がTのものだけを取り出すメソッドです。なので、キャスト出来ない要素に関しては切り捨てられます。「型がTのものだけを取り出す」ので当然戻り値はIEnumerable<T>です。つまり、結果的にはIEnumerableからIEnumerable<T>に変換を行うことができるメソッドなんです!

出来た~\(^0^)/
Animal型に定義されている動物の、鳴き声が3文字のものだけをList<string>として保存したい」をLInqを使って実装できました!

まとめ

最後まで読んでいただきありがとうございました!
他にも良い方法などあればお気軽にコメントお願いします^^

OfTypeメソッドは他にも使い道がありそうなので覚えていて損はないですね!
list.Where(v => v is (TestType)); 
という書き方をするなら
list.OfType<TestType>(); 
と書き直した方がスマートかもしれませんね!

OfTypeメソッドのリファレンスが見たい方はこちら

良かったらコメントしてね!