拡張メソッドとは?
前回の【.NET】リフレクション入門同様、C#(.NET)の基本的な機能の入門です。
今回は「拡張メソッド」編
例のごとくとりあえず公式ドキュメントを見てみましょう。
拡張メソッドを使用すると、新規の派生型の作成、再コンパイル、または元の型の変更を行うことなく既存の型にメソッドを "追加" できます。 拡張メソッドは静的メソッドですが、拡張された型のインスタンス メソッドのように呼び出します。 C#、F#、Visual Basic で作成されたクライアント コードの場合は、拡張メソッドの呼び出しと、型で定義されているメソッドの呼び出しに明確な違いはありません。
なるほど。
既存のクラス(型)に再コンパイルや継承、変更を加えることなく新しいメソッドを追加できるということですね🤔
拡張メソッドの代表的な例が、リストの操作でよく使うLINQ。
静的メソッドをあたかもインスタンスメソッドかのように呼び出すことができるわけです。
メリット
では、拡張メソッドを使うことによって得られるメリットはどのようなものか?
- 既存コードに変更を加えることなく機能追加が可能
- メソッドチェーン利用による可読性の向上
- 汎用的な型の拡張メソッドは複数のプロジェクトで利用可能
- インターフェースに拡張メソッドを定義できるため、柔軟な設計が可能
- 利用者側からは静的クラスの実装を気にする必要がない
自身実装のクラスというよりは、本来メソッドが追加できないクラスやインターフェースに利用されることが多いようですね。(フレームワークに既存のクラス、サードパーティ製のクラス、列挙型など)
デメリット
もちろんデメリットや使用上の注意点もあります。
- 使いすぎると本来のクラスとのメソッドと見分けが困難になり、コードが複雑になる
- 本来のクラスに同名のメソッドが存在する場合、拡張メソッドが呼び出されなくなる(インスタンスメソッドの呼び出しが優先される)
- 拡張メソッドは継承不可のため、派生クラスでのオーバライドができない
- 本来のクラスの内部実装に依存する拡張メソッドはカプセル化を損なう可能性がある
実装例
何はともあれ、とりあえず使ってみよう!
ということで、適当に思いついたものを実装してみました。
拡張メソッドを実装する場合は、静的クラスの静的メソッドとして定義します。
最初の引数に拡張したいクラスをthis
修飾子を使って指定すればOK👌
【要件】
コンソールアプリを開いた時点の時間によって出力する文字列を以下のパターンで変える。
- 05:00 ~ 09:59 → "早起きですね!朝活でもしちゃう?"
- 10:00 ~ 14:59 → "何時まで寝てるの!?"
- 15:00 ~ 20:59 → "休日を無駄にしたね…"
- 21:00 ~ 04:59 → "夜行性?"
【実装】
public class Main
{
public Main()
{
GetUp(DateTime.Now.TimeOfDay);
}
private static void GetUp(TimeSpan wakeUpTime)
{
if (wakeUpTime.IsEarly())
{
Console.WriteLine("早起きですね!朝活でもしちゃう?");
}
else if (wakeUpTime.IsLate())
{
Console.WriteLine("何時まで寝てるの!?");
}
else if (wakeUpTime.IsWaste())
{
Console.WriteLine("休日を無駄にしたね…");
}
else
{
Console.WriteLine("夜行性?");
}
}
}
/// <summary>
/// TimeSpan型の拡張メソッド群
/// </summary>
public static class TimeSpanExtensions
{
/// <summary> 05:00~09:59ならtrue </summary>
public static bool IsEarly(this TimeSpan time)
{
return IsWithinRange(
time,
new TimeSpan(5, 0, 0),
new TimeSpan(9, 59, 0));
}
/// <summary> 10:00~14:59ならtrue </summary>
public static bool IsLate(this TimeSpan time)
{
return IsWithinRange(
time,
new TimeSpan(10, 0, 0),
new TimeSpan(14, 59, 0));
}
/// <summary> 15:00~20:59ならtrue </summary>
public static bool IsWaste(this TimeSpan time)
{
return IsWithinRange(
time,
new TimeSpan(15, 0, 0),
new TimeSpan(20, 59, 0));
}
/// <summary> 対象の時刻が上限下限の範囲内が否か </summary>
private static bool IsWithinRange(TimeSpan target, TimeSpan min, TimeSpan max)
{
return
target >= min &&
target <= max;
}
}
参考