C#には、クラスの継承をしなくても、既存クラスにメソッドを追加する機能、いわゆる「拡張メソッド」を作ることが出来ます。
拡張メソッドをうまく使えば、コーディングの効率を高めるとともに、可読性も向上するなどのメリットは大きく、使わないのはもったいないです。
そこで、この記事では、拡張メソッドに興味があって、これから使いたいと思う方を対象に、その作り方、使い方、注意点を分かりやすく解説していきます。
拡張メソッドとは
既存のクラスに対して、継承を使わずに追加したメソッドを「拡張メソッド」と呼んでいます。
自分が作ったクラスであれば、後から自由にメソッドが作れますが、C#に組み込まれている String や List 、Array などのクラスについては、おいそれとメソッドを追加できませんよね。
拡張メソッドを使うと、簡単な記述で、これらのクラスに自分の好きなメソッドを追加することができます。
一旦拡張メソッドを作ってしまうと、Visual Studio 上の コード補完機能(IntelliSense)で使えるようになるので、めちゃめちゃ便利になります。
拡張メソッドの作り方
拡張メソッドは、通常の public static クラスとほぼ同じ記述で作成可能です。ただし1点だけ違いがあって、第一引数に this というキーワードを付けなければなりません。
記述ルール
this を付けることにより、拡張元のオブジェクトが第一引数に渡されます。従って、第一引数のデータ型は、拡張元のオブジェクトのデータ型を指定しなければなりません。
例えば、DataTableの拡張メソッドとして double 型の値を返す Sumメソッドを作成する場合、次のようになります。
public static double Sum( this DataTable dt )
拡張メソッドは既存のクラス(例えば DataTable や List など)に新たなメソッドを追加(拡張)するものなので、拡張メソッド名(クラス名)に付けた名前を使うことはありませんが、通常は拡張元のクラスを名前に含めるなど、分かり易い名前をつけておきます。
戻り値は任意のデータ型(オブジェクト)を返すことが可能です。Listの拡張メソッドを作ったからと言って、List型のデータを返す必要はありません。Array、String、double、DataTableなど、あらゆる型を返すことが可能です。
サンプルソース
下記は、StringExtention という名前の拡張メソッドを作った場合のサンプルソースです。
public static class StringExtention
{
/// <summary>
/// 文字列をbool型に変換する。変換できなかった場合、 alternativeValue を返す。
/// alternateValueの初期値は false。
/// </summary>
/// <param name="self"></param>
/// <param name="alternativeValue"></param>
/// <returns></returns>
public static bool ToBool(this string str, bool? alternativeValue = null)
{
return (bool.TryParse(str, out bool value) ? value : alternativeValue??false);
}
/// <summary>
/// 文字列をint型に変換する。変換できなかった場合、 alternativeValue を返す。
/// alternateValueの初期値は 0。
/// </summary>
/// <param name="str"></param>
/// <param name="alternativeValue"></param>
/// <returns></returns>
public static int ToInt(this string str, int? alternativeValue = null)
{
return (int.TryParse(str, out int value) ? value : alternativeValue??0);
}
/// <summary>
/// 文字列をdouble型に変換する。変換できなかった場合、 alternativeValue を返す。
/// alternateValueの初期値は double.NaN。
/// </summary>
/// <param name="str"></param>
/// <param name="alternativeValue"></param>
/// <returns></returns>
public static double ToDouble(this string str, double? alternativeValue = null)
{
return (double.TryParse(str, out double value) ? value : alternativeValue??double.NaN);
}
/// <summary>
/// 文字列を日付型に変換する。変換できなかった場合、 alternativeValue を返す。
/// alternateValueの初期値は DateTime.MaxValue。
/// </summary>
/// <param name="str"></param>
/// <param name="alternativeValue"></param>
/// <returns></returns>
public static DateTime ToDateTime(this string str, DateTime? alternativeValue = null)
{
return (DateTime.TryParse(str, out DateTime value) ? value : alternativeValue??DateTime.MinValue);
}
/// <summary>
/// 文字列をString.Formatのフォーマット文字に見立て、可変長引数を渡して文字列を作成する
/// </summary>
/// <param name="str"></param>
/// <param name="args"></param>
/// <returns></returns>
public static string Args(this string str, params object[] args)
{
return string.Format(str, args);
}
}
拡張メソッドの使い方
拡張メソッドは普通のメソッドとまったく同じなので、ピリオド(.)を付けて記述します。
例えば、先ほど紹介したサンプルの中にあるToDoubleというメソッドがありました。これは、引数1(this string str)と 引数2(alternativeValue) が指定されています。
public static double ToDouble(this string str, double? alternativeValue = null)
{
return (double.TryParse(str, out double value) ? value : alternativeValue??double.NaN);
}
これを使う場合、次の様に記述します。
double val = "12345.8".ToDouble(0);
引数1は拡張メソッドの定義の際に使用するものなので、使う場合は引数2以降を渡します。
ToDoubleの引数1(定義の際の引数2)に0を指定していますので、 alternativeValue は0が代入されます。勿論、省略可能なので省略することも可能です。
拡張メソッドが適用できるクラス
拡張メソッドは static class にする必要はありますが、拡張元のクラスは static でなくても構いません。
例えば、次のようなケースでは、new して使う MyClass に対して拡張メソッドを作っていますが、ちゃんとコード補完機能(IntelliSense)に拡張メソッド(MyExtention)が表示されています。
拡張メソッドの注意点
便利な拡張メソッドですが、次の様なリスクもあります。
- 不用意に使いすぎると、どこにどんな拡張メソッドをつくったかが分からなくなる
- 拡張メソッドと同じメソッド名が、元のクラスに実装されたとき、元のクラスのメソッドが優先されます。
- 派生クラス全てに対して拡張メソッドが有効になるので、派生メソッド側で同じメソッド名が作られても実行されない
便利だからと言って乱用しすぎないよう注意しましょう。
まとめ
今回は拡張メソッドについて解説しました。
継承を使わず既存のクラスにメソッドを拡張できる点は、非常に便利です。
乱用は禁物ですが、効果的に使うと生産性や可読性の向上に役立ちます。
作り方は簡単なので、是非ご活用下さい。