【C#】Genericsで型変換を使ってみた
こんにちはAmagoです。
C#のコードを書き始めて1年くらい経ちます。最近になって、Genericsの使い方について理解できたので、記事にしようかと思います。
私がプログラムを書くときに心がけていることがいくつかあります。
見やすく、保守し易いことです。
チーム開発ではこれに当てはまらないソースをよく見かけます。
同じような条件分岐が至る所に散りばめられており、仕様変更のたびに全部に修正を加えたりと・・・
それをそのままコピーペーストして無駄な処理が倍々とかね・・・
今回は自分が担当してた機能でジェネリクスを使ったらソースがかなりコンパクトになったので、こんな使い方もあるんだぞというご紹介です。
ジェネリクスを使ってみよう
今回はint、long、Decimalのnull許容型の変数を別の数値型に変換するプログラムを作ってみよう思います。
今回作成したサンプルソースはGitHubで公開してあります。よかったら見てください。
GitHub - hamaguchi-amago/GenericsSample: ジェネリクス サンプルソース ブログの記事で作りました。
intのnull許容型からintに変換してみる(ジェネリクスは使っていません。)
まずは、intのnull許容型からintに変換してみましょう、この場合はわざわざメソッドを作る必要はなく。Convert.ToInt32を使えばいいと思います。
[Sample1.cs]
using System; namespace GenericsSample { class Sample1 { public void Main() { int? data = null; var result = Convert.ToInt32(data); Console.WriteLine("Type : " + result.GetType()); Console.WriteLine("Value: " + result.ToString()); } } }
Type : System.Int32 Value: 0
intとlongも変換してみる(こちらも、ジェネリクスを使っていません)
次は、intとlongを戻り値として受け取りたい場合です。
こちらも、専用のメソッドがあるのでわざわざ独自のメソッドを作る必要はなさそうですね。
[Sample2.cs]
using System; namespace GenericsSample { class Sample2 { public void Main() { int? data1 = null; long? data2 = null; var result1 = Convert.ToInt32(data1); var result2 = Convert.ToInt64(data2); WriteData(result1); WriteData(result2); } private void WriteData(object data) { Console.WriteLine("Type : " + data.GetType()); Console.WriteLine("Value: " + data.ToString()); Console.WriteLine(); } } }
Type : System.Int32 Value: 0 Type : System.Int64 Value: 0
戻り値も引数も指定したい場合は?
ここでとうとうジェネリクスの出番です。よくよく考えると今回の例はジェネリクスを使わなくてもConvertを使えば
良かったかもなぁと執筆中に気が付いてしまいましたが・・・
nullを受け取ったら1を返すとか100をかえす仕様だったり。
メソッド内でほかのメソッドを呼ぶ必要がある場合は無駄ではないよね?
と自分を慰めつつ続きを紹介していきます。
ジェネリックを使って作ったメソッドがこちらです。
[Sample3.cs]
private T2 Convert<T1, T2>(T1 data) { if (data == null) { return (T2)(dynamic)(0); } else { return (T2)(dynamic)data; } }
ジェネリックの便利さがいまいちピンときていないかもしれないので、具体的に例を出します。
例えば、引数に『int?、long?、Decimal?』の3パターンの型のデータを受け取り、それぞれ『int、long、Decimal』の3パターンの型を返すメソッドを作る場合について考えてみます。
ジェネリックを使わずに型を指定すると3×3で9個、ここにもう一つ型が増えたら4×4で16個も同じようなメソッドを作らないといけませんよね。
そんなめんどくさいことをやらなくても型を指定して呼び出してあげれば、たった数ステップで仕様を満たすことが出来るのです。
次は呼び出し方についてみてみましょう。呼び出し方はこちらになります。
[Sample3.cs]
private void Pattern1() { Int = null; Long = null; Decimal = null; var result_1_1 = Convert<int?, int>(Int); var result_1_2 = Convert<long?, long>(Long); var result_1_3 = Convert<Decimal?, Decimal>(Decimal); WriteData(result_1_1); WriteData(result_1_2); WriteData(result_1_3); } private void WriteData(object data) { Console.WriteLine("Type : " + data.GetType()); Console.WriteLine("Value: " + data.ToString()); Console.WriteLine(); }
Type : System.Int32 Value: 0 Type : System.Int64 Value: 0 Type : System.Decimal Value: 0
[Sample3.cs]
private void Pattern2() { Int = null; Long = null; Decimal = null; var result_2_1 = Convert<int?, Decimal>(Int); var result_2_2 = Convert<long?, int>(Long); var result_2_3 = Convert<Decimal?, int>(Decimal); WriteData(result_2_1); WriteData(result_2_2); WriteData(result_2_3); }
Type : System.Decimal Value: 0 Type : System.Int32 Value: 0 Type : System.Int32 Value: 0
もちろん、null以外に数値が設定されている場合も正しく動きます。ちなみに変数Intに設定されている値の2,147,483,647はintの最大値です。
[Sample3.cs]
private void Pattern3() { Int = 2147483647; Long = 2147483648; Decimal = 1000; var result_3_1 = Convert<int?, int>(Int); var result_3_2 = Convert<long?, long>(Long); var result_3_3 = Convert<Decimal?, Decimal>(Decimal); WriteData(result_3_1); WriteData(result_3_2); WriteData(result_3_3); }
Type : System.Int32 Value: 2147483647 Type : System.Int64 Value: 2147483648 Type : System.Decimal Value: 1000