既存のクラスに新しいメソッドを追加したい場面があります。しかし、対象が外部ライブラリの型や sealed クラスなら、クラス定義そのものを編集できないことがあります。そういうときに使うのが拡張メソッドです。
static class と this を使った定義方法を理解できるprivate / protected メンバーにアクセスできない理由を理解できるたとえば既存のクラスに便利なメソッドを足したくても、そのクラスの定義を変更できないことがあります。
sealed クラスで継承できないこのようなとき、別の static class にメソッドを書くことで、元の型にメソッドが増えたように扱えます。
書式:拡張メソッド
1
2
3
4
5
6
7
static class 拡張クラス名
{
public static 戻り値の型 メソッド名(this 対象の型 引数名, 追加引数)
{
処理
}
}
| 要素 | 説明 |
|---|---|
static class |
拡張メソッドを置くクラス。必ず static |
public static |
拡張メソッド本体。必ず static |
this |
第一引数が拡張対象であることを示す |
対象の型 |
拡張したい型 |
引数名 |
拡張先のインスタンスを受け取る名前 |
追加引数 |
必要なら第2引数以降に追加する引数 |
this を付けた第一引数が、「どの型を拡張するか」を決めます。
呼び出し側では、通常のインスタンスメソッドと同じように 変数.M() と書けます。
コンパイラはこれを、内部では AExtensions.M(a) のような static メソッド呼び出しに変換します。つまり、拡張メソッドは糖衣構文です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A
{
public int Value;
}
static class AExtensions
{
public static void M(this A a)
{
Console.WriteLine($"AExtensions.M: {a.Value}");
}
public static void N(this A a, int x)
{
Console.WriteLine($"AExtensions.N: {a.Value + x}");
}
}
var a = new A { Value = 42 };
a.M();
a.N(10);
1
2
AExtensions.M: 42
AExtensions.N: 52
a.M() と書いていますが、実体は AExtensions.M(a) です。a.N(10) の実体は AExtensions.N(a, 10) です。
拡張メソッドは、対象クラスの内部に入り込む仕組みではありません。そのため、使えるのは対象型の public メンバー が中心です。
private メンバーにはアクセスできないprotected メンバーにもアクセスできないつまり、拡張メソッドは「見た目をインスタンスメソッドに近づける書き方」であり、アクセス制御を無視する仕組みではありません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A
{
public int Value;
}
// ❌ NG: 拡張メソッドを置くクラスは static 必須
// class AExtensions
// {
// public static void M(this A a) { Console.WriteLine(a.Value); }
// }
// ✅ OK: static class に定義する
static class AExtensions
{
public static void M(this A a) { Console.WriteLine($"AExtensions.M: {a.Value}"); }
}
private メンバーにアクセスしようとする1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A
{
private int _value = 42;
public int Value { get { return _value; } }
}
static class AExtensions
{
// ❌ NG: private メンバーにはアクセスできない
// public static void M(this A a) { Console.WriteLine(a._value); }
// ✅ OK: public メンバーを使う
public static void M(this A a) { Console.WriteLine($"AExtensions.M: {a.Value}"); }
}
static class の中に public static 戻り値 M(this 型 引数名, ...) と定義する変数.M() と書けるが、内部では static メソッド呼び出しに変換されるprivate / protected メンバーにはアクセスできない以下の問いに答えられるか確認しましょう。
次のコードの出力結果は何になりますか?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A
{
public int Value;
}
static class AExtensions
{
public static void M(this A a)
{
Console.WriteLine($"AExtensions.M: {a.Value}");
}
}
var a = new A { Value = 7 };
a.M();
A の Value に x を足した結果を表示する拡張メソッド N(this A a, int x) を書いてください。sealed クラスなど、元のクラスを直接編集できないのに便利なメソッドを追加したい場面です。1
AExtensions.M: 7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class A
{
public int Value;
}
static class AExtensions
{
public static void N(this A a, int x)
{
Console.WriteLine($"AExtensions.N: {a.Value + x}");
}
}
var a = new A { Value = 10 };
a.N(5);
1
AExtensions.N: 15
継承 では、既存クラスのメンバーを引き継いで新しいクラスを作る仕組みを学びます。